浏览代码

Point light shadows first push. Working solution based on rendering 6 different shadow maps.
Made a PointlLightShadowRenderer and a PointLightShadowFilter.
- This will need a lot of refactoring as a lot of code is duplicated with the PSSMShadowRenderer
- Also i plan to change the Shadow map rendering to a cubemap instead of 6 separate textures.
Added a cornell box model and a test case


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

rem..om 13 年之前
父节点
当前提交
66ddbb654d

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

@@ -108,6 +108,9 @@ MaterialDef Phong Lighting {
         Texture2D ShadowMap1
         Texture2D ShadowMap1
         Texture2D ShadowMap2
         Texture2D ShadowMap2
         Texture2D ShadowMap3
         Texture2D ShadowMap3
+        //pointLights
+        Texture2D ShadowMap4
+        Texture2D ShadowMap5
         
         
         Float ShadowIntensity
         Float ShadowIntensity
         Vector4 Splits
         Vector4 Splits
@@ -117,6 +120,10 @@ MaterialDef Phong Lighting {
         Matrix4 LightViewProjectionMatrix1
         Matrix4 LightViewProjectionMatrix1
         Matrix4 LightViewProjectionMatrix2
         Matrix4 LightViewProjectionMatrix2
         Matrix4 LightViewProjectionMatrix3
         Matrix4 LightViewProjectionMatrix3
+        //pointLight
+        Matrix4 LightViewProjectionMatrix4
+        Matrix4 LightViewProjectionMatrix5   
+        Vector3 LightPos
 
 
         Float PCFEdge
         Float PCFEdge
         Float ShadowMapSize
         Float ShadowMapSize
@@ -209,6 +216,8 @@ MaterialDef Phong Lighting {
             COLOR_MAP : ColorMap
             COLOR_MAP : ColorMap
             SHADOWMAP_SIZE : ShadowMapSize
             SHADOWMAP_SIZE : ShadowMapSize
             FADE : FadeInfo
             FADE : FadeInfo
+            PSSM : Splits
+            POINTLIGHT : LightViewProjectionMatrix5
         }
         }
 
 
         ForcedRenderState {
         ForcedRenderState {
@@ -235,6 +244,8 @@ MaterialDef Phong Lighting {
             COLOR_MAP : ColorMap
             COLOR_MAP : ColorMap
             SHADOWMAP_SIZE : ShadowMapSize
             SHADOWMAP_SIZE : ShadowMapSize
             FADE : FadeInfo
             FADE : FadeInfo
+            PSSM : Splits
+            POINTLIGHT : LightViewProjectionMatrix5
         }
         }
 
 
         ForcedRenderState {
         ForcedRenderState {

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

@@ -18,6 +18,12 @@ uniform mat4 m_LightViewProjectionMatrix1;
 uniform mat4 m_LightViewProjectionMatrix2;
 uniform mat4 m_LightViewProjectionMatrix2;
 uniform mat4 m_LightViewProjectionMatrix3;
 uniform mat4 m_LightViewProjectionMatrix3;
 
 
+#ifdef POINTLIGHT
+    uniform vec3 m_LightPos;
+    uniform mat4 m_LightViewProjectionMatrix4;
+    uniform mat4 m_LightViewProjectionMatrix5;
+#endif
+
 #ifdef FADE
 #ifdef FADE
 uniform vec2 m_FadeInfo;
 uniform vec2 m_FadeInfo;
 #endif
 #endif
@@ -47,23 +53,53 @@ void main(){
     vec4 projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos;
     vec4 projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos;
     vec4 projCoord2 = biasMat * m_LightViewProjectionMatrix2 * worldPos;
     vec4 projCoord2 = biasMat * m_LightViewProjectionMatrix2 * worldPos;
     vec4 projCoord3 = biasMat * m_LightViewProjectionMatrix3 * worldPos;
     vec4 projCoord3 = biasMat * m_LightViewProjectionMatrix3 * worldPos;
+    #ifdef POINTLIGHT
+       vec4 projCoord4 = biasMat * m_LightViewProjectionMatrix4 * worldPos;
+       vec4 projCoord5 = biasMat * m_LightViewProjectionMatrix5 * worldPos;
+    #endif
 
 
-
-    float shadowPosition = m_ViewProjectionMatrixRow2.x * worldPos.x +  m_ViewProjectionMatrixRow2.y * worldPos.y +  m_ViewProjectionMatrixRow2.z * worldPos.z +  m_ViewProjectionMatrixRow2.w;
-    
     float shadow = 1.0;
     float shadow = 1.0;
-    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);
-    }
+    #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);
+            }
+         }                  
+    #endif   
 
 
     #ifdef FADE
     #ifdef FADE
       shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y));    
       shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y));    

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

@@ -8,6 +8,10 @@ MaterialDef Post Shadow {
         Texture2D ShadowMap1
         Texture2D ShadowMap1
         Texture2D ShadowMap2
         Texture2D ShadowMap2
         Texture2D ShadowMap3
         Texture2D ShadowMap3
+        //pointLights
+        Texture2D ShadowMap4
+        Texture2D ShadowMap5
+        Vector3 LightPos
         
         
         Float ShadowIntensity
         Float ShadowIntensity
         Vector4 Splits
         Vector4 Splits
@@ -16,7 +20,10 @@ MaterialDef Post Shadow {
         Matrix4 LightViewProjectionMatrix0
         Matrix4 LightViewProjectionMatrix0
         Matrix4 LightViewProjectionMatrix1
         Matrix4 LightViewProjectionMatrix1
         Matrix4 LightViewProjectionMatrix2
         Matrix4 LightViewProjectionMatrix2
-        Matrix4 LightViewProjectionMatrix3     
+        Matrix4 LightViewProjectionMatrix3  
+        //pointLight
+        Matrix4 LightViewProjectionMatrix4
+        Matrix4 LightViewProjectionMatrix5   
 
 
         Float PCFEdge
         Float PCFEdge
 
 
@@ -48,6 +55,8 @@ MaterialDef Post Shadow {
             PCFEDGE : PCFEdge
             PCFEDGE : PCFEdge
             SHADOWMAP_SIZE : ShadowMapSize
             SHADOWMAP_SIZE : ShadowMapSize
             FADE : FadeInfo
             FADE : FadeInfo
+            PSSM : Splits
+            POINTLIGHT : LightViewProjectionMatrix5
         }
         }
       
       
     }
     }
@@ -66,6 +75,8 @@ MaterialDef Post Shadow {
             PCFEDGE : PCFEdge
             PCFEDGE : PCFEdge
             SHADOWMAP_SIZE : ShadowMapSize
             SHADOWMAP_SIZE : ShadowMapSize
             FADE : FadeInfo
             FADE : FadeInfo
+            PSSM : Splits
+            POINTLIGHT : LightViewProjectionMatrix5
         }
         }
       
       
     }
     }

+ 51 - 14
engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter15.frag

@@ -20,6 +20,12 @@ uniform mat4 m_LightViewProjectionMatrix1;
 uniform mat4 m_LightViewProjectionMatrix2;
 uniform mat4 m_LightViewProjectionMatrix2;
 uniform mat4 m_LightViewProjectionMatrix3;
 uniform mat4 m_LightViewProjectionMatrix3;
 
 
+#ifdef POINTLIGHT
+    uniform vec3 m_LightPos;
+    uniform mat4 m_LightViewProjectionMatrix4;
+    uniform mat4 m_LightViewProjectionMatrix5;
+#endif
+
 #ifdef FADE
 #ifdef FADE
 uniform vec2 m_FadeInfo;
 uniform vec2 m_FadeInfo;
 #endif
 #endif
@@ -47,22 +53,53 @@ vec4 main_multiSample(in int numSample){
     vec4 projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos;
     vec4 projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos;
     vec4 projCoord2 = biasMat * m_LightViewProjectionMatrix2 * worldPos;
     vec4 projCoord2 = biasMat * m_LightViewProjectionMatrix2 * worldPos;
     vec4 projCoord3 = biasMat * m_LightViewProjectionMatrix3 * worldPos;
     vec4 projCoord3 = biasMat * m_LightViewProjectionMatrix3 * worldPos;
+    #ifdef POINTLIGHT
+       vec4 projCoord4 = biasMat * m_LightViewProjectionMatrix4 * worldPos;
+       vec4 projCoord5 = biasMat * m_LightViewProjectionMatrix5 * worldPos;
+    #endif
 
 
-    float shadowPosition = m_ViewProjectionMatrixRow2.x * worldPos.x +  m_ViewProjectionMatrixRow2.y * worldPos.y +  m_ViewProjectionMatrixRow2.z * worldPos.z +  m_ViewProjectionMatrixRow2.w;
-  
     float shadow = 1.0;
     float shadow = 1.0;
-    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);
-    }
+    #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);
+            }
+         }                  
+    #endif   
 
 
     #ifdef FADE
     #ifdef FADE
       shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y));    
       shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y));    

+ 50 - 13
engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.frag

@@ -1,11 +1,21 @@
 #import "Common/ShaderLib/PssmShadows.glsllib"
 #import "Common/ShaderLib/PssmShadows.glsllib"
 
 
+#ifdef PSSM
 varying float shadowPosition;
 varying float shadowPosition;
+#endif
+
 varying vec4 projCoord0;
 varying vec4 projCoord0;
 varying vec4 projCoord1;
 varying vec4 projCoord1;
 varying vec4 projCoord2;
 varying vec4 projCoord2;
 varying vec4 projCoord3;
 varying vec4 projCoord3;
 
 
+#ifdef POINTLIGHT
+    varying vec4 projCoord4;
+    varying vec4 projCoord5;
+    uniform vec3 m_LightPos;
+    varying vec4 worldPos;
+#endif
+
 #ifdef DISCARD_ALPHA
 #ifdef DISCARD_ALPHA
     #ifdef COLOR_MAP
     #ifdef COLOR_MAP
         uniform sampler2D m_ColorMap;
         uniform sampler2D m_ColorMap;
@@ -35,19 +45,46 @@ void main(){
     #endif
     #endif
      
      
    float shadow = 1.0;
    float shadow = 1.0;
-    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);
-    }
-    
+    #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
     #ifdef FADE
       shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y));    
       shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y));    
     #endif
     #endif

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

@@ -8,6 +8,9 @@ MaterialDef Post Shadow {
         Texture2D ShadowMap1
         Texture2D ShadowMap1
         Texture2D ShadowMap2
         Texture2D ShadowMap2
         Texture2D ShadowMap3
         Texture2D ShadowMap3
+        //pointLights
+        Texture2D ShadowMap4
+        Texture2D ShadowMap5
         
         
         Float ShadowIntensity
         Float ShadowIntensity
         Vector4 Splits
         Vector4 Splits
@@ -17,6 +20,10 @@ MaterialDef Post Shadow {
         Matrix4 LightViewProjectionMatrix1
         Matrix4 LightViewProjectionMatrix1
         Matrix4 LightViewProjectionMatrix2
         Matrix4 LightViewProjectionMatrix2
         Matrix4 LightViewProjectionMatrix3
         Matrix4 LightViewProjectionMatrix3
+        //pointLight
+        Matrix4 LightViewProjectionMatrix4
+        Matrix4 LightViewProjectionMatrix5
+        Vector3 LightPos
 
 
         Float PCFEdge
         Float PCFEdge
 
 
@@ -38,6 +45,8 @@ MaterialDef Post Shadow {
             PCFEDGE : PCFEdge
             PCFEDGE : PCFEdge
             SHADOWMAP_SIZE : ShadowMapSize
             SHADOWMAP_SIZE : ShadowMapSize
             FADE : FadeInfo
             FADE : FadeInfo
+            PSSM : Splits
+            POINTLIGHT : LightViewProjectionMatrix5
         }
         }
 
 
         RenderState {
         RenderState {
@@ -62,6 +71,8 @@ MaterialDef Post Shadow {
             PCFEDGE : PCFEdge
             PCFEDGE : PCFEdge
             SHADOWMAP_SIZE : ShadowMapSize
             SHADOWMAP_SIZE : ShadowMapSize
             FADE : FadeInfo
             FADE : FadeInfo
+            PSSM : Splits
+            POINTLIGHT : LightViewProjectionMatrix5
         }
         }
 
 
         RenderState {
         RenderState {

+ 19 - 2
engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.vert

@@ -11,7 +11,17 @@ varying vec4 projCoord1;
 varying vec4 projCoord2;
 varying vec4 projCoord2;
 varying vec4 projCoord3;
 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;
 varying float shadowPosition;
+#endif
 
 
 varying vec2 texCoord;
 varying vec2 texCoord;
 
 
@@ -30,9 +40,12 @@ const mat4 biasMat = mat4(0.5, 0.0, 0.0, 0.0,
 void main(){
 void main(){
     gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
     gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);
 
 
-    shadowPosition = gl_Position.z;
+    #ifdef PSSM
+        shadowPosition = gl_Position.z;
+        vec4 worldPos=vec4(0.0);
+    #endif
     // get the vertex in world space
     // get the vertex in world space
-    vec4 worldPos = g_WorldMatrix * vec4(inPosition, 1.0);
+    worldPos = g_WorldMatrix * vec4(inPosition, 1.0);
 
 
     #ifdef DISCARD_ALPHA
     #ifdef DISCARD_ALPHA
        texCoord = inTexCoord;
        texCoord = inTexCoord;
@@ -42,4 +55,8 @@ void main(){
     projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos;
     projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos;
     projCoord2 = biasMat * m_LightViewProjectionMatrix2 * worldPos;
     projCoord2 = biasMat * m_LightViewProjectionMatrix2 * worldPos;
     projCoord3 = biasMat * m_LightViewProjectionMatrix3 * worldPos;
     projCoord3 = biasMat * m_LightViewProjectionMatrix3 * worldPos;
+    #ifdef POINTLIGHT
+        projCoord4 = biasMat * m_LightViewProjectionMatrix4 * worldPos;
+        projCoord5 = biasMat * m_LightViewProjectionMatrix5 * worldPos;
+    #endif
 }
 }

+ 57 - 13
engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM15.frag

@@ -1,12 +1,23 @@
 #import "Common/ShaderLib/PssmShadows15.glsllib"
 #import "Common/ShaderLib/PssmShadows15.glsllib"
 
 
 out vec4 outFragColor;
 out vec4 outFragColor;
+
+#ifdef PSSM
 in float shadowPosition;
 in float shadowPosition;
+#endif
+
 in vec4 projCoord0;
 in vec4 projCoord0;
 in vec4 projCoord1;
 in vec4 projCoord1;
 in vec4 projCoord2;
 in vec4 projCoord2;
 in vec4 projCoord3;
 in vec4 projCoord3;
 
 
+#ifdef POINTLIGHT
+    in vec4 projCoord4;
+    in vec4 projCoord5;
+    uniform vec3 m_LightPos;
+    in vec4 worldPos;
+#endif
+
 #ifdef DISCARD_ALPHA
 #ifdef DISCARD_ALPHA
     #ifdef COLOR_MAP
     #ifdef COLOR_MAP
         uniform sampler2D m_ColorMap;
         uniform sampler2D m_ColorMap;
@@ -35,20 +46,53 @@ void main(){
         }
         }
     #endif
     #endif
 
 
-    
     float shadow = 1.0;
     float shadow = 1.0;
-    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); 
-    }
+    #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
     #ifdef FADE
       shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y));    
       shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y));    

+ 13 - 1
engine/src/core-data/Common/ShaderLib/PssmShadows.glsllib

@@ -3,7 +3,7 @@
     #define SHADOWCOMPARE(tex,coord) shadow2DProj(tex, coord).r 
     #define SHADOWCOMPARE(tex,coord) shadow2DProj(tex, coord).r 
 #else
 #else
     #define SHADOWMAP sampler2D
     #define SHADOWMAP sampler2D
-    #define SHADOWCOMPARE(tex,coord) step(coord.z, texture2DProj(tex, coord).r)
+    #define SHADOWCOMPARE(tex,coord) step(coord.z / coord.w, texture2DProj(tex, coord).r)
 #endif
 #endif
 
 
 #if FILTER_MODE == 0
 #if FILTER_MODE == 0
@@ -35,8 +35,14 @@ uniform SHADOWMAP m_ShadowMap0;
 uniform SHADOWMAP m_ShadowMap1;
 uniform SHADOWMAP m_ShadowMap1;
 uniform SHADOWMAP m_ShadowMap2;
 uniform SHADOWMAP m_ShadowMap2;
 uniform SHADOWMAP m_ShadowMap3;
 uniform SHADOWMAP m_ShadowMap3;
+#ifdef POINTLIGHT
+uniform SHADOWMAP m_ShadowMap4;
+uniform SHADOWMAP m_ShadowMap5;
+#endif
 
 
+#ifdef PSSM
 uniform vec4 m_Splits;
 uniform vec4 m_Splits;
+#endif
 
 
 uniform float m_ShadowIntensity;
 uniform float m_ShadowIntensity;
 
 
@@ -53,10 +59,16 @@ float Shadow_DoShadowCompare(in SHADOWMAP tex, vec4 projCoord){
 }
 }
 
 
 float Shadow_BorderCheck(in vec2 coord){
 float Shadow_BorderCheck(in vec2 coord){
+#ifdef PSSM
     // Fastest, "hack" method (uses 4-5 instructions)
     // Fastest, "hack" method (uses 4-5 instructions)
     vec4 t = vec4(coord.xy, 0.0, 1.0);
     vec4 t = vec4(coord.xy, 0.0, 1.0);
     t = step(t.wwxy, t.xyzz);
     t = step(t.wwxy, t.xyzz);
     return dot(t,t);
     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_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){

+ 15 - 3
engine/src/core-data/Common/ShaderLib/PssmShadows15.glsllib

@@ -9,8 +9,8 @@
     #define SHADOWGATHER(tex,coord) textureGather(tex, coord.xy, coord.z)
     #define SHADOWGATHER(tex,coord) textureGather(tex, coord.xy, coord.z)
 #else
 #else
     #define SHADOWMAP sampler2D
     #define SHADOWMAP sampler2D
-    #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 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 SHADOWGATHER(tex,coord) step(coord.z, textureGather(tex, coord.xy))
     #define SHADOWGATHER(tex,coord) step(coord.z, textureGather(tex, coord.xy))
 #endif
 #endif
 
 
@@ -45,18 +45,30 @@ uniform SHADOWMAP m_ShadowMap0;
 uniform SHADOWMAP m_ShadowMap1;
 uniform SHADOWMAP m_ShadowMap1;
 uniform SHADOWMAP m_ShadowMap2;
 uniform SHADOWMAP m_ShadowMap2;
 uniform SHADOWMAP m_ShadowMap3;
 uniform SHADOWMAP m_ShadowMap3;
+#ifdef POINTLIGHT
+uniform SHADOWMAP m_ShadowMap4;
+uniform SHADOWMAP m_ShadowMap5;
+#endif
 
 
+#ifdef PSSM
 uniform vec4 m_Splits;
 uniform vec4 m_Splits;
+#endif
 uniform float m_ShadowIntensity;
 uniform float m_ShadowIntensity;
 
 
 const vec2 pixSize2 = vec2(1.0 / SHADOWMAP_SIZE);
 const vec2 pixSize2 = vec2(1.0 / SHADOWMAP_SIZE);
 float shadowBorderScale = 1.0;
 float shadowBorderScale = 1.0;
 
 
 float Shadow_BorderCheck(in vec2 coord){
 float Shadow_BorderCheck(in vec2 coord){
+#ifdef PSSM    
     // Fastest, "hack" method (uses 4-5 instructions)
     // Fastest, "hack" method (uses 4-5 instructions)
     vec4 t = vec4(coord.xy, 0.0, 1.0);
     vec4 t = vec4(coord.xy, 0.0, 1.0);
     t = step(t.wwxy, t.xyzz);
     t = step(t.wwxy, t.xyzz);
     return dot(t,t);
     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_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){
@@ -90,7 +102,7 @@ float Shadow_DoBilinear_2x2(in SHADOWMAP tex, in vec4 projCoord){
         gather.y = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 0));
         gather.y = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 0));
         gather.z = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(0, 1));
         gather.z = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(0, 1));
         gather.w = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 1));
         gather.w = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 1));
-   #endif
+    #endif
 
 
    vec2 f = fract( projCoord.xy * SHADOWMAP_SIZE );
    vec2 f = fract( projCoord.xy * SHADOWMAP_SIZE );
    vec2 mx = mix( gather.xz, gather.yw, f.x );
    vec2 mx = mix( gather.xz, gather.yw, f.x );

+ 241 - 0
engine/src/core/com/jme3/shadow/PointLightShadowFilter.java

@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.shadow;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.light.PointLight;
+import com.jme3.material.Material;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Vector4f;
+import com.jme3.post.Filter;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.shadow.PointLightShadowRenderer.CompareMode;
+import com.jme3.shadow.PointLightShadowRenderer.FilterMode;
+import com.jme3.texture.FrameBuffer;
+import java.io.IOException;
+
+/** 
+ * 
+ * This Filter does basically the same as a PointLightShadowRenderer except it renders 
+ * the post shadow pass as a fulscreen quad pass instead of a geometry pass.
+ * It's mostly faster than PointLightShadowRenderer as long as you have more than a about ten shadow recieving objects.
+ * The expense is the draw back that the shadow Recieve mode set on spatial is ignored.
+ * So basically all and only objects that render depth in the scene receive shadows.
+ * See this post for more details http://jmonkeyengine.org/groups/general-2/forum/topic/silly-question-about-shadow-rendering/#post-191599
+ * 
+ * API is basically the same as the PssmShadowRenderer;
+ * 
+ * @author Rémy Bouquet aka Nehon
+ */
+public class PointLightShadowFilter extends Filter {
+
+    private PointLightShadowRenderer plRenderer;
+    private ViewPort viewPort;
+
+    /**
+     * Creates a PSSM Shadow Filter 
+     * More info on the technique at <a href="http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html">http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html</a>
+     * @param manager the application asset manager
+     * @param size the size of the rendered shadowmaps (512,1024,2048, etc...)
+     * @param nbSplits the number of shadow maps rendered (the more shadow maps the more quality, the less fps). 
+     */
+    public PointLightShadowFilter(AssetManager manager, int size) {
+        super("Post Shadow");
+        material = new Material(manager, "Common/MatDefs/Shadow/PostShadowFilter.j3md");
+        plRenderer = new PointLightShadowRenderer(manager, size, material);
+        plRenderer.needsfallBackMaterial = true;
+    }
+
+    @Override
+    protected Material getMaterial() {
+        return material;
+    }
+
+    @Override
+    protected boolean isRequiresDepthTexture() {
+        return true;
+    }
+
+    public Material getShadowMaterial() {
+        return material;
+    }
+    Vector4f tmpv = new Vector4f();
+
+    @Override
+    protected void preFrame(float tpf) {
+        plRenderer.preFrame(tpf);
+        material.setMatrix4("ViewProjectionMatrixInverse", viewPort.getCamera().getViewProjectionMatrix().invert());
+        Matrix4f m = viewPort.getCamera().getViewProjectionMatrix();
+        material.setVector4("ViewProjectionMatrixRow2", tmpv.set(m.m20, m.m21, m.m22, m.m23));
+
+    }
+
+    @Override
+    protected void postQueue(RenderQueue queue) {
+        plRenderer.postQueue(queue);
+    }
+
+    @Override
+    protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) {
+        plRenderer.setPostShadowParams();
+    }
+
+    @Override
+    protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
+        plRenderer.initialize(renderManager, vp);
+        this.viewPort = vp;
+    }
+
+     /**
+     * gets the point light used to cast shadows with this processor
+     *
+     * @return the point light
+     */
+    public PointLight getLight() {
+        return plRenderer.getLight();
+    }
+
+    /**
+     * sets the light to use for casting shadows with this processor
+     *
+     * @param light the point light
+     */
+    public void setLight(PointLight light) {
+        plRenderer.setLight(light);
+    }   
+
+    /**
+     * returns the shdaow intensity
+     * @see #setShadowIntensity(float shadowIntensity)
+     * @return shadowIntensity
+     */
+    public float getShadowIntensity() {
+        return plRenderer.getShadowIntensity();
+    }
+
+    /**
+     * Set the shadowIntensity, the value should be between 0 and 1,
+     * a 0 value gives a bright and invisilble shadow,
+     * a 1 value gives a pitch black shadow,
+     * default is 0.7
+     * @param shadowIntensity the darkness of the shadow
+     */
+    final public void setShadowIntensity(float shadowIntensity) {
+        plRenderer.setShadowIntensity(shadowIntensity);
+    }
+
+    /**
+     * returns the edges thickness <br>
+     * @see #setEdgesThickness(int edgesThickness)
+     * @return edgesThickness
+     */
+    public int getEdgesThickness() {
+        return plRenderer.getEdgesThickness();
+    }
+
+    /**
+     * Sets the shadow edges thickness. default is 1, setting it to lower values can help to reduce the jagged effect of the shadow edges
+     * @param edgesThickness 
+     */
+    public void setEdgesThickness(int edgesThickness) {
+        plRenderer.setEdgesThickness(edgesThickness);
+    }
+
+    /**
+     * returns true if the PssmRenderer flushed the shadow queues
+     * @return flushQueues
+     */
+    public boolean isFlushQueues() {
+        return plRenderer.isFlushQueues();
+    }
+
+    /**
+     * Set this to false if you want to use several PssmRederers to have multiple shadows cast by multiple light sources.
+     * Make sure the last PssmRenderer in the stack DO flush the queues, but not the others
+     * @param flushQueues 
+     */
+    public void setFlushQueues(boolean flushQueues) {
+        plRenderer.setFlushQueues(flushQueues);
+    }
+
+    /**
+     * sets the shadow compare mode see {@link CompareMode} for more info
+     * @param compareMode 
+     */
+    final public void setCompareMode(CompareMode compareMode) {
+        plRenderer.setCompareMode(compareMode);
+    }
+    
+    /**
+     * Sets the filtering mode for shadow edges see {@link FilterMode} for more info
+     * @param filterMode 
+     */
+    final public void setFilterMode(FilterMode filterMode) {
+        plRenderer.setFilterMode(filterMode);
+    }
+    
+     /**
+     * Define the length over which the shadow will fade out when using a shadowZextend
+     * @param length the fade length in world units
+     */
+    public void setShadowZFadeLength(float length){
+       plRenderer.setShadowZFadeLength(length);        
+    }
+    
+     /**
+     * get the length over which the shadow will fade out when using a shadowZextend
+     * @return the fade length in world units
+     */
+    public float getShadowZFadeLength(){       
+        return plRenderer.getShadowZFadeLength();        
+    }
+
+    @Override
+    public void write(JmeExporter ex) throws IOException {
+        super.write(ex);
+        OutputCapsule oc = ex.getCapsule(this);
+
+    }
+
+    @Override
+    public void read(JmeImporter im) throws IOException {
+        super.read(im);
+        InputCapsule ic = im.getCapsule(this);
+
+    }
+}

+ 753 - 0
engine/src/core/com/jme3/shadow/PointLightShadowRenderer.java

@@ -0,0 +1,753 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.jme3.shadow;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.light.PointLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.post.SceneProcessor;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.Caps;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.Renderer;
+import com.jme3.renderer.ViewPort;
+import com.jme3.renderer.queue.GeometryList;
+import com.jme3.renderer.queue.OpaqueComparator;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.debug.WireFrustum;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture.MagFilter;
+import com.jme3.texture.Texture.MinFilter;
+import com.jme3.texture.Texture.ShadowCompareMode;
+import com.jme3.texture.Texture2D;
+import com.jme3.ui.Picture;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * PssmShadow renderer use Parrallel Split Shadow Mapping technique (pssm)<br>
+ * It splits the view frustum in several parts and compute a shadow map for each
+ * one.<br> splits are distributed so that the closer they are from the camera,
+ * the smaller they are to maximize the resolution used of the shadow map.<br>
+ * This result in a better quality shadow than standard shadow mapping.<br> for
+ * more informations on this read this <a
+ * href="http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html">http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html</a><br>
+ * <p/>
+ * @author Rémy Bouquet aka Nehon
+ */
+public class PointLightShadowRenderer implements SceneProcessor {
+
+    /**
+     * <code>FilterMode</code> specifies how shadows are filtered
+     */
+    public enum FilterMode {
+
+        /**
+         * Shadows are not filtered. Nearest sample is used, causing in blocky
+         * shadows.
+         */
+        Nearest,
+        /**
+         * Bilinear filtering is used. Has the potential of being hardware
+         * accelerated on some GPUs
+         */
+        Bilinear,
+        /**
+         * Dither-based sampling is used, very cheap but can look bad at low
+         * resolutions.
+         */
+        Dither,
+        /**
+         * 4x4 percentage-closer filtering is used. Shadows will be smoother at
+         * the cost of performance
+         */
+        PCF4,
+        /**
+         * 8x8 percentage-closer filtering is used. Shadows will be smoother at
+         * the cost of performance
+         */
+        PCFPOISSON,
+        /**
+         * 8x8 percentage-closer filtering is used. Shadows will be smoother at
+         * the cost of performance
+         */
+        PCF8
+    }
+
+    /**
+     * Specifies the shadow comparison mode
+     */
+    public enum CompareMode {
+
+        /**
+         * Shadow depth comparisons are done by using shader code
+         */
+        Software,
+        /**
+         * Shadow depth comparisons are done by using the GPU's dedicated
+         * shadowing pipeline.
+         */
+        Hardware;
+    }
+    protected final int CAM_NUMBER = 6;
+    protected PointLight light;
+    //common  
+    protected float shadowMapSize;
+    protected float shadowIntensity = 0.7f;
+    protected float zFarOverride = 0;
+    protected RenderManager renderManager;
+    protected ViewPort viewPort;
+    protected FrameBuffer[] shadowFB;
+    protected Texture2D[] shadowMaps;
+    protected Texture2D dummyTex;
+    protected Camera[] shadowCams;
+    protected Material preshadowMat;
+    protected Material postshadowMat;
+    protected GeometryList splitOccluders = new GeometryList(new OpaqueComparator());
+    protected Matrix4f[] lightViewProjectionsMatrices;
+    protected boolean noOccluders = false;
+    protected AssetManager assetManager;
+    protected boolean debug = false;
+    protected float edgesThickness = 1.0f;
+    protected FilterMode filterMode;
+    protected CompareMode compareMode;
+    protected Picture[] dispPic;
+    protected boolean flushQueues = true;
+    // define if the fallback material should be used.
+    protected boolean needsfallBackMaterial = false;
+    //Name of the post material technique
+    protected String postTechniqueName = "PostShadow";
+    //flags to know when to change params in the materials
+    protected boolean applyHWShadows = true;
+    protected boolean applyFilterMode = true;
+    protected boolean applyPCFEdge = true;
+    protected boolean applyShadowIntensity = true;
+    //a list of material of the post shadow queue geometries.
+    protected List<Material> matCache = new ArrayList<Material>();
+    //Holding the info for fading shadows in the far distance 
+    protected Vector2f fadeInfo;
+    protected float fadeLength;
+    protected boolean applyFadeInfo = false;
+
+    /**
+     * Create a PSSM Shadow Renderer More info on the technique at <a
+     * href="http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html">http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html</a>
+     *
+     * @param manager the application asset manager
+     * @param size the size of the rendered shadowmaps (512,1024,2048, etc...)
+     * @param nbSplits the number of shadow maps rendered (the more shadow maps
+     * the more quality, the less fps).
+     * @param nbSplits the number of shadow maps rendered (the more shadow maps
+     * the more quality, the less fps).
+     */
+    public PointLightShadowRenderer(AssetManager manager, int size) {
+        this(manager, size, new Material(manager, "Common/MatDefs/Shadow/PostShadowPSSM.j3md"));
+    }
+
+    /**
+     * Create a PSSM Shadow Renderer More info on the technique at <a
+     * href="http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html">http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html</a>
+     *
+     * @param manager the application asset manager
+     * @param size the size of the rendered shadowmaps (512,1024,2048, etc...)
+     * @param nbSplits the number of shadow maps rendered (the more shadow maps
+     * the more quality, the less fps).
+     * @param postShadowMat the material used for post shadows if you need to
+     * override it
+     */
+    protected PointLightShadowRenderer(AssetManager manager, int size, Material postShadowMat) {
+
+        this.postshadowMat = postShadowMat;
+        assetManager = manager;
+        shadowMapSize = size;
+
+        shadowFB = new FrameBuffer[CAM_NUMBER];
+        shadowMaps = new Texture2D[CAM_NUMBER];
+        dispPic = new Picture[CAM_NUMBER];
+        lightViewProjectionsMatrices = new Matrix4f[CAM_NUMBER];
+
+        //DO NOT COMMENT THIS (it prevent the OSX incomplete read buffer crash)
+        dummyTex = new Texture2D(size, size, Format.RGBA8);
+
+        preshadowMat = new Material(manager, "Common/MatDefs/Shadow/PreShadow.j3md");
+        postshadowMat.setFloat("ShadowMapSize", size);
+
+        shadowCams = new Camera[CAM_NUMBER];
+
+        for (int i = 0; i < CAM_NUMBER; i++) {
+            lightViewProjectionsMatrices[i] = new Matrix4f();
+            shadowFB[i] = new FrameBuffer(size, size, 1);
+            shadowMaps[i] = new Texture2D(size, size, Format.Depth);
+
+            shadowFB[i].setDepthTexture(shadowMaps[i]);
+
+            //DO NOT COMMENT THIS (it prevent the OSX incomplete read buffer crash)
+            shadowFB[i].setColorTexture(dummyTex);
+
+            postshadowMat.setTexture("ShadowMap" + i, shadowMaps[i]);
+
+            //quads for debuging purpose
+            dispPic[i] = new Picture("Picture" + i);
+            dispPic[i].setTexture(manager, shadowMaps[i], false);
+
+            shadowCams[i] = new Camera(size, size);
+        }
+
+        setCompareMode(CompareMode.Hardware);
+        setFilterMode(FilterMode.Bilinear);
+        setShadowIntensity(0.7f);
+
+    }
+
+    /**
+     * Sets the filtering mode for shadow edges see {@link FilterMode} for more
+     * info
+     *
+     * @param filterMode
+     */
+    final public void setFilterMode(FilterMode filterMode) {
+        if (filterMode == null) {
+            throw new NullPointerException();
+        }
+
+        if (this.filterMode == filterMode) {
+            return;
+        }
+
+        this.filterMode = filterMode;
+        postshadowMat.setInt("FilterMode", filterMode.ordinal());
+        postshadowMat.setFloat("PCFEdge", edgesThickness);
+        if (compareMode == CompareMode.Hardware) {
+            for (Texture2D shadowMap : shadowMaps) {
+                if (filterMode == FilterMode.Bilinear) {
+                    shadowMap.setMagFilter(MagFilter.Bilinear);
+                    shadowMap.setMinFilter(MinFilter.BilinearNoMipMaps);
+                } else {
+                    shadowMap.setMagFilter(MagFilter.Nearest);
+                    shadowMap.setMinFilter(MinFilter.NearestNoMipMaps);
+                }
+            }
+        }
+        applyFilterMode = true;
+    }
+
+    public FilterMode getFilterMode() {
+        return filterMode;
+    }
+       
+    /**
+     * sets the shadow compare mode see {@link CompareMode} for more info
+     *
+     * @param compareMode
+     */
+    final public void setCompareMode(CompareMode compareMode) {
+        if (compareMode == null) {
+            throw new NullPointerException();
+        }
+
+        if (this.compareMode == compareMode) {
+            return;
+        }
+
+        this.compareMode = compareMode;
+        for (Texture2D shadowMap : shadowMaps) {
+            if (compareMode == CompareMode.Hardware) {
+                shadowMap.setShadowCompareMode(ShadowCompareMode.LessOrEqual);
+                if (filterMode == FilterMode.Bilinear) {
+                    shadowMap.setMagFilter(MagFilter.Bilinear);
+                    shadowMap.setMinFilter(MinFilter.BilinearNoMipMaps);
+                } else {
+                    shadowMap.setMagFilter(MagFilter.Nearest);
+                    shadowMap.setMinFilter(MinFilter.NearestNoMipMaps);
+                }
+            } else {
+                shadowMap.setShadowCompareMode(ShadowCompareMode.Off);
+                shadowMap.setMagFilter(MagFilter.Nearest);
+                shadowMap.setMinFilter(MinFilter.NearestNoMipMaps);
+            }
+        }
+        postshadowMat.setBoolean("HardwareShadows", compareMode == CompareMode.Hardware);
+        applyHWShadows = true;
+    }
+
+    //debug function that create a displayable frustrum
+    private Geometry createFrustum(Vector3f[] pts, int i) {
+        WireFrustum frustum = new WireFrustum(pts);
+        Geometry frustumMdl = new Geometry("f", frustum);
+        frustumMdl.setCullHint(Spatial.CullHint.Never);
+        frustumMdl.setShadowMode(ShadowMode.Off);
+        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+        mat.getAdditionalRenderState().setWireframe(true);
+        frustumMdl.setMaterial(mat);
+        switch (i) {
+            case 0:
+                frustumMdl.getMaterial().setColor("Color", ColorRGBA.Pink);
+                break;
+            case 1:
+                frustumMdl.getMaterial().setColor("Color", ColorRGBA.Red);
+                break;
+            case 2:
+                frustumMdl.getMaterial().setColor("Color", ColorRGBA.Green);
+                break;
+            case 3:
+                frustumMdl.getMaterial().setColor("Color", ColorRGBA.Blue);
+                break;
+            case 4:
+                frustumMdl.getMaterial().setColor("Color", ColorRGBA.Yellow);
+                break;
+            case 5:
+                frustumMdl.getMaterial().setColor("Color", ColorRGBA.Gray);
+                break;
+            default:
+                frustumMdl.getMaterial().setColor("Color", ColorRGBA.White);
+                break;
+        }
+
+        frustumMdl.updateGeometricState();
+        return frustumMdl;
+    }
+
+    public void initialize(RenderManager rm, ViewPort vp) {
+        renderManager = rm;
+        viewPort = vp;
+        //checking for caps to chosse the appropriate post material technique
+        if (renderManager.getRenderer().getCaps().contains(Caps.GLSL150)) {
+            postTechniqueName = "PostShadow15";
+        } else {
+            postTechniqueName = "PostShadow";
+        }
+    }
+
+    public boolean isInitialized() {
+        return viewPort != null;
+    }
+
+    @SuppressWarnings("fallthrough")
+    public void postQueue(RenderQueue rq) {
+        if (light == null) {
+            throw new IllegalStateException("The light can't be null for a " + this.getClass().getName());
+        }
+        GeometryList occluders = rq.getShadowQueueContent(ShadowMode.Cast);
+        if (occluders.size() == 0) {
+            return;
+        }
+
+        GeometryList receivers = rq.getShadowQueueContent(ShadowMode.Receive);
+        if (receivers.size() == 0) {
+            return;
+        }
+
+        Camera viewCam = viewPort.getCamera();
+
+        float zFar = zFarOverride;
+        if (zFar == 0) {
+            zFar = viewCam.getFrustumFar();
+        }
+
+        //We prevent computing the frustum points and splits with zeroed or negative near clip value
+        //float frustumNear = Math.max(viewCam.getFrustumNear(), 0.001f);
+        // ShadowUtil.updateFrustumPoints(viewCam, frustumNear, zFar, 1.0f, points);
+
+        updateShadowCams();
+
+        Renderer r = renderManager.getRenderer();
+        renderManager.setForcedMaterial(preshadowMat);
+        renderManager.setForcedTechnique("PreShadow");
+
+        for (int i = 0; i < CAM_NUMBER; i++) {
+
+            //Updating shadow cam with curent split frustra
+
+            ShadowUtil.getOccludersInCamFrustum(occluders, shadowCams[i], splitOccluders);
+
+            //saving light view projection matrix for this split
+            lightViewProjectionsMatrices[i].set(shadowCams[i].getViewProjectionMatrix());
+            renderManager.setCamera(shadowCams[i], false);
+
+            if (debug && frustums[i].getParent() == null) {
+                ((Node) viewPort.getScenes().get(0)).attachChild(frustums[i]);
+            }
+
+            r.setFrameBuffer(shadowFB[i]);
+            r.clearBuffers(false, true, false);
+
+            //      System.out.println("Face " + i + " nb occl " + splitOccluders.size());
+            // render shadow casters to shadow map
+            viewPort.getQueue().renderShadowQueue(splitOccluders, renderManager, shadowCams[i], true);
+        }
+        if (flushQueues) {
+            occluders.clear();
+        }
+        //restore setting for future rendering
+        r.setFrameBuffer(viewPort.getOutputFrameBuffer());
+        renderManager.setForcedMaterial(null);
+        renderManager.setForcedTechnique(null);
+        renderManager.setCamera(viewCam, false);
+
+    }
+
+    //debug only : displays depth shadow maps
+    protected void displayShadowMap(Renderer r) {
+        Camera cam = viewPort.getCamera();
+        renderManager.setCamera(cam, true);
+        int h = cam.getHeight();
+        for (int i = 0; i < dispPic.length; i++) {
+            dispPic[i].setPosition((128 * i) + (150 + 64 * (i + 1)), h / 20f);
+            dispPic[i].setWidth(128);
+            dispPic[i].setHeight(128);
+            dispPic[i].updateGeometricState();
+            renderManager.renderGeometry(dispPic[i]);
+        }
+        renderManager.setCamera(cam, false);
+    }
+
+    /**
+     * For dubuging purpose Allow to "snapshot" the current frustrum to the
+     * scene
+     */
+    public void displayDebug() {
+        debug = true;
+    }
+
+    public void postFrame(FrameBuffer out) {
+
+        if (debug) {
+            displayShadowMap(renderManager.getRenderer());
+        }
+        if (!noOccluders) {
+            //setting params to recieving geometry list
+            setMatParams();
+
+            Camera cam = viewPort.getCamera();
+            //some materials in the scene does not have a post shadow technique so we're using the fall back material
+            if (needsfallBackMaterial) {
+                renderManager.setForcedMaterial(postshadowMat);
+            }
+
+            //forcing the post shadow technique and render state
+            renderManager.setForcedTechnique(postTechniqueName);
+
+            //rendering the post shadow pass
+            viewPort.getQueue().renderShadowQueue(ShadowMode.Receive, renderManager, cam, flushQueues);
+
+            //resetting renderManager settings
+            renderManager.setForcedTechnique(null);
+            renderManager.setForcedMaterial(null);
+            renderManager.setCamera(cam, false);
+
+        }
+
+    }
+
+    private void updateShadowCams() {
+
+
+        //bottom
+        shadowCams[0].setAxes(Vector3f.UNIT_X.mult(-1f), Vector3f.UNIT_Z.mult(-1f), Vector3f.UNIT_Y.mult(-1f));
+
+        //top
+        shadowCams[1].setAxes(Vector3f.UNIT_X.mult(-1f), Vector3f.UNIT_Z, Vector3f.UNIT_Y);
+
+        //forward
+        shadowCams[2].setAxes(Vector3f.UNIT_X.mult(-1f), Vector3f.UNIT_Y, Vector3f.UNIT_Z.mult(-1f));
+
+        //backward
+        shadowCams[3].setAxes(Vector3f.UNIT_X, Vector3f.UNIT_Y, Vector3f.UNIT_Z);
+
+        //left
+        shadowCams[4].setAxes(Vector3f.UNIT_Z, Vector3f.UNIT_Y, Vector3f.UNIT_X.mult(-1f));
+
+        //right
+        shadowCams[5].setAxes(Vector3f.UNIT_Z.mult(-1f), Vector3f.UNIT_Y, Vector3f.UNIT_X);
+
+        for (int i = 0; i < CAM_NUMBER; i++) {
+            shadowCams[i].setFrustumPerspective(90f, 1f, 0.1f, light.getRadius());
+            shadowCams[i].setLocation(light.getPosition());
+            shadowCams[i].update();
+            shadowCams[i].updateViewProjection();
+        }
+
+        if (debug && frustums == null) {
+            frustums = new Geometry[CAM_NUMBER];
+            Vector3f[] points = new Vector3f[8];
+            for (int i = 0; i < 8; i++) {
+                points[i] = new Vector3f();
+            }
+            for (int i = 0; i < CAM_NUMBER; i++) {
+                ShadowUtil.updateFrustumPoints2(shadowCams[i], points);
+                frustums[i] = createFrustum(points, i);
+            }
+        }
+
+
+    }
+    private Geometry[] frustums = null;
+
+    private void setMatParams() {
+
+        GeometryList l = viewPort.getQueue().getShadowQueueContent(ShadowMode.Receive);
+
+        //iteration throught all the geometries of the list to gather the materials
+
+        matCache.clear();
+        for (int i = 0; i < l.size(); i++) {
+            Material mat = l.get(i).getMaterial();
+            //checking if the material has the post technique and adding it to the material cache
+            if (mat.getMaterialDef().getTechniqueDef(postTechniqueName) != null) {
+                if (!matCache.contains(mat)) {
+                    matCache.add(mat);
+                }
+            } else {
+                needsfallBackMaterial = true;
+            }
+        }
+
+        //iterating through the mat cache and setting the parameters
+        for (Material mat : matCache) {
+
+            if (mat.getParam("ShadowMapSize") == null) {
+                mat.setFloat("ShadowMapSize", shadowMapSize);
+            }
+            for (int j = 0; j < CAM_NUMBER; j++) {
+                mat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]);
+            }
+            mat.setVector3("LightPos", light.getPosition());
+            if (mat.getParam("ShadowMap0") == null) {
+                for (int j = 0; j < CAM_NUMBER; j++) {
+                    mat.setTexture("ShadowMap" + j, shadowMaps[j]);
+                }
+            }
+            if (applyHWShadows || mat.getParam("HardwareShadows") == null) {
+                mat.setBoolean("HardwareShadows", compareMode == CompareMode.Hardware);
+            }
+            if (applyFilterMode || mat.getParam("FilterMode") == null) {
+                mat.setInt("FilterMode", filterMode.ordinal());
+            }
+            if (mat.getParam("PCFEdge") == null || applyPCFEdge) {
+                mat.setFloat("PCFEdge", edgesThickness);
+            }
+
+            if (mat.getParam("ShadowIntensity") == null || applyShadowIntensity) {
+                mat.setFloat("ShadowIntensity", shadowIntensity);
+            }
+
+            if (fadeInfo != null && mat.getParam("FadeInfo") == null || applyFadeInfo) {
+                mat.setVector2("FadeInfo", fadeInfo);
+            }
+
+        }
+        
+        applyHWShadows = false;
+        applyFilterMode = false;
+        applyPCFEdge = false;
+        applyShadowIntensity = false;
+        applyFadeInfo = false;
+        
+        //At least one material of the receiving geoms does not support the post shadow techniques
+        //so we fall back to the forced material solution (transparent shadows won't be supported for these objects)
+        if (needsfallBackMaterial) {
+            setPostShadowParams();
+        }
+
+    }
+
+    protected void setPostShadowParams() {
+        for (int j = 0; j < CAM_NUMBER; j++) {
+            postshadowMat.setMatrix4("LightViewProjectionMatrix" + j, lightViewProjectionsMatrices[j]);
+        }
+        postshadowMat.setVector3("LightPos", light.getPosition());
+    }
+
+    public void preFrame(float tpf) {
+    }
+
+    public void cleanup() {
+    }
+
+    public void reshape(ViewPort vp, int w, int h) {
+    }
+
+    /**
+     * returns the shdaow intensity
+     *
+     * @see #setShadowIntensity(float shadowIntensity)
+     * @return shadowIntensity
+     */
+    public float getShadowIntensity() {
+        return shadowIntensity;
+    }
+
+    /**
+     * Set the shadowIntensity, the value should be between 0 and 1, a 0 value
+     * gives a bright and invisilble shadow, a 1 value gives a pitch black
+     * shadow, default is 0.7
+     *
+     * @param shadowIntensity the darkness of the shadow
+     */
+    final public void setShadowIntensity(float shadowIntensity) {
+        this.shadowIntensity = shadowIntensity;
+        postshadowMat.setFloat("ShadowIntensity", shadowIntensity);
+        applyShadowIntensity = true;
+    }
+
+    /**
+     * How far the shadows are rendered in the view
+     *
+     * @see #setShadowZExtend(float zFar)
+     * @return shadowZExtend
+     */
+    public float getShadowZExtend() {
+        return zFarOverride;
+    }
+
+    /**
+     * Set the distance from the eye where the shadows will be rendered default
+     * value is dynamicaly computed to the shadow casters/receivers union bound
+     * zFar, capped to view frustum far value.
+     *
+     * @param zFar the zFar values that override the computed one
+     */
+    public void setShadowZExtend(float zFar) {
+        if (fadeInfo != null) {
+            fadeInfo.set(zFar - fadeLength, 1f / fadeLength);
+        }
+        this.zFarOverride = zFar;
+
+    }
+
+    /**
+     * returns the edges thickness
+     *
+     * @see #setEdgesThickness(int edgesThickness)
+     * @return edgesThickness
+     */
+    public int getEdgesThickness() {
+        return (int) (edgesThickness * 10);
+    }
+
+    /**
+     * Sets the shadow edges thickness. default is 1, setting it to lower values
+     * can help to reduce the jagged effect of the shadow edges
+     *
+     * @param edgesThickness
+     */
+    public void setEdgesThickness(int edgesThickness) {
+        this.edgesThickness = Math.max(1, Math.min(edgesThickness, 10));
+        this.edgesThickness *= 0.1f;
+        postshadowMat.setFloat("PCFEdge", edgesThickness);
+        applyPCFEdge = true;
+    }
+
+    /**
+     * returns true if the PssmRenderer flushed the shadow queues
+     *
+     * @return flushQueues
+     */
+    public boolean isFlushQueues() {
+        return flushQueues;
+    }
+
+    /**
+     * Set this to false if you want to use several PssmRederers to have
+     * multiple shadows cast by multiple light sources. Make sure the last
+     * PssmRenderer in the stack DO flush the queues, but not the others
+     *
+     * @param flushQueues
+     */
+    public void setFlushQueues(boolean flushQueues) {
+        this.flushQueues = flushQueues;
+    }
+
+    /**
+     * Define the length over which the shadow will fade out when using a
+     * shadowZextend This is useful to make dynamic shadows fade into baked
+     * shadows in the distance.
+     *
+     * @param length the fade length in world units
+     */
+    public void setShadowZFadeLength(float length) {
+        if (length == 0) {
+            fadeInfo = null;
+            fadeLength = 0;
+            postshadowMat.clearParam("FadeInfo");
+        } else {
+            if (zFarOverride == 0) {
+                fadeInfo = new Vector2f(0, 0);
+            } else {
+                fadeInfo = new Vector2f(zFarOverride - length, 1.0f / length);
+            }
+            fadeLength = length;
+            postshadowMat.setVector2("FadeInfo", fadeInfo);
+        }
+    }
+
+    /**
+     * get the length over which the shadow will fade out when using a
+     * shadowZextend
+     *
+     * @return the fade length in world units
+     */
+    public float getShadowZFadeLength() {
+        if (fadeInfo != null) {
+            return zFarOverride - fadeInfo.x;
+        }
+        return 0f;
+    }
+
+    /**
+     * gets the point light used to cast shadows with this processor
+     *
+     * @return the point light
+     */
+    public PointLight getLight() {
+        return light;
+    }
+
+    /**
+     * sets the light to use for casting shadows with this processor
+     *
+     * @param light the point light
+     */
+    public void setLight(PointLight light) {
+        this.light = light;
+        updateShadowCams();
+    }
+}

+ 36 - 13
engine/src/core/com/jme3/shadow/ShadowUtil.java

@@ -44,6 +44,7 @@ import com.jme3.util.TempVars;
 import static java.lang.Math.max;
 import static java.lang.Math.max;
 import static java.lang.Math.min;
 import static java.lang.Math.min;
 import java.util.ArrayList;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 import java.util.List;
 
 
 /**
 /**
@@ -65,19 +66,17 @@ public class ShadowUtil {
      */
      */
     public static void updateFrustumPoints2(Camera viewCam, Vector3f[] points) {
     public static void updateFrustumPoints2(Camera viewCam, Vector3f[] points) {
         int w = viewCam.getWidth();
         int w = viewCam.getWidth();
-        int h = viewCam.getHeight();
-        float n = viewCam.getFrustumNear();
-        float f = viewCam.getFrustumFar();
-
-        points[0].set(viewCam.getWorldCoordinates(new Vector2f(0, 0), n));
-        points[1].set(viewCam.getWorldCoordinates(new Vector2f(0, h), n));
-        points[2].set(viewCam.getWorldCoordinates(new Vector2f(w, h), n));
-        points[3].set(viewCam.getWorldCoordinates(new Vector2f(w, 0), n));
-
-        points[4].set(viewCam.getWorldCoordinates(new Vector2f(0, 0), f));
-        points[5].set(viewCam.getWorldCoordinates(new Vector2f(0, h), f));
-        points[6].set(viewCam.getWorldCoordinates(new Vector2f(w, h), f));
-        points[7].set(viewCam.getWorldCoordinates(new Vector2f(w, 0), f));
+        int h = viewCam.getHeight();    
+
+        points[0].set(viewCam.getWorldCoordinates(new Vector2f(0, 0), 0));
+        points[1].set(viewCam.getWorldCoordinates(new Vector2f(0, h), 0));
+        points[2].set(viewCam.getWorldCoordinates(new Vector2f(w, h), 0));
+        points[3].set(viewCam.getWorldCoordinates(new Vector2f(w, 0), 0));
+
+        points[4].set(viewCam.getWorldCoordinates(new Vector2f(0, 0), 1));
+        points[5].set(viewCam.getWorldCoordinates(new Vector2f(0, h), 1));
+        points[6].set(viewCam.getWorldCoordinates(new Vector2f(w, h), 1));
+        points[7].set(viewCam.getWorldCoordinates(new Vector2f(w, 0), 1));
     }
     }
 
 
     /**
     /**
@@ -494,4 +493,28 @@ public class ShadowUtil {
         shadowCam.setProjectionMatrix(result);
         shadowCam.setProjectionMatrix(result);
 
 
     }
     }
+    
+     /**
+     * Updates the shadow camera to properly contain the given
+     * points (which contain the eye camera frustum corners) and the
+     * shadow occluder objects.
+     * 
+     * @param occluders
+     * @param shadowCam
+     * @param points
+     */
+    public static void getOccludersInCamFrustum(GeometryList occluders,
+            Camera shadowCam,
+            GeometryList splitOccluders) {
+        for (int i = 0; i < occluders.size(); i++) {
+            Geometry g = occluders.get(i);
+            int planeState = shadowCam.getPlaneState();
+            shadowCam.setPlaneState(0);
+            if (shadowCam.contains(g.getWorldBound()) != Camera.FrustumIntersect.Outside) {
+                splitOccluders.add(g);
+            }
+            shadowCam.setPlaneState(planeState);
+        }
+
+    }
 }
 }

+ 237 - 0
engine/src/test/jme3test/light/TestPointLightShadows.java

@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2009-2012 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jme3test.light;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.font.BitmapText;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.PointLight;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.shape.Box;
+import com.jme3.scene.shape.Sphere;
+import com.jme3.shadow.PointLightShadowFilter;
+import com.jme3.shadow.PointLightShadowRenderer;
+
+public class TestPointLightShadows extends SimpleApplication implements ActionListener {
+
+    public static void main(String[] args) {
+        TestPointLightShadows app = new TestPointLightShadows();
+        app.start();
+    }
+    Node lightNode;
+    private boolean hardwareShadows = true;
+    PointLightShadowRenderer plsr ;
+    PointLightShadowFilter plsf;
+
+    @Override
+    public void simpleInitApp() {
+        flyCam.setMoveSpeed(10);
+        cam.setLocation(new Vector3f(0.040581334f, 1.7745866f, 6.155161f));
+        cam.setRotation(new Quaternion(4.3868728E-5f, 0.9999293f, -0.011230096f, 0.0039059948f));
+
+
+        Node scene = (Node) assetManager.loadModel("Models/Test/CornellBox.j3o");
+        scene.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
+        rootNode.attachChild(scene);
+        rootNode.getChild("Cube").setShadowMode(RenderQueue.ShadowMode.Receive);
+        lightNode = (Node) rootNode.getChild("Lamp");
+        Geometry lightMdl = new Geometry("Light", new Sphere(10, 10, 0.1f));
+        //Geometry  lightMdl = new Geometry("Light", new Box(.1f,.1f,.1f));
+        lightMdl.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m"));
+        lightMdl.setShadowMode(RenderQueue.ShadowMode.Off);
+        lightNode.attachChild(lightMdl);
+        //lightMdl.setLocalTranslation(lightNode.getLocalTranslation());
+
+
+        Geometry box = new Geometry("box", new Box(0.2f, 0.2f, 0.2f));
+        //Geometry  lightMdl = new Geometry("Light", new Box(.1f,.1f,.1f));
+        box.setMaterial(assetManager.loadMaterial("Common/Materials/RedColor.j3m"));
+        box.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
+        rootNode.attachChild(box);
+        box.setLocalTranslation(-1f, 0.5f, -2);
+
+
+
+        plsr = new PointLightShadowRenderer(assetManager, 512);
+        plsr.setLight((PointLight) scene.getLocalLightList().get(0));
+        plsr.setFilterMode(PointLightShadowRenderer.FilterMode.Nearest);
+    //   plsr.displayDebug();
+        viewPort.addProcessor(plsr);
+
+
+        plsf = new PointLightShadowFilter(assetManager, 512);
+        plsf.setLight((PointLight) scene.getLocalLightList().get(0));
+        plsf.setFilterMode(PointLightShadowRenderer.FilterMode.Nearest);
+        plsf.setEnabled(false);
+        
+        FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
+        fpp.addFilter(plsf);   
+        viewPort.addProcessor(fpp);
+
+        initUIAndInputs();
+    }
+    
+     BitmapText shadowTypeText;
+     BitmapText shadowCompareText;
+     BitmapText shadowFilterText;
+     BitmapText shadowIntensityText;
+     final static String TYPE_TEXT = "(Space) Shadow type : ";
+     final static String COMPARE_TEXT = "(enter) Shadow compare ";
+     final static String FILTERING_TEXT = "(f) Edge filtering : ";
+     final static String INTENSITY_TEXT = "(t:up, g:down) Shadow intensity : ";
+     
+     private void initUIAndInputs() {     
+        guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
+        shadowTypeText = createText();
+        shadowCompareText = createText();
+        shadowFilterText = createText();
+        shadowIntensityText = createText();
+        
+        shadowTypeText.setText(TYPE_TEXT+"Processor");
+        shadowCompareText.setText(COMPARE_TEXT+(hardwareShadows?"Hardware":"Software"));
+        shadowFilterText.setText(FILTERING_TEXT+plsr.getFilterMode().toString());
+        shadowIntensityText.setText(INTENSITY_TEXT+plsr.getShadowIntensity());        
+
+        shadowTypeText.setLocalTranslation(10, cam.getHeight()-20, 0);
+        shadowCompareText.setLocalTranslation(10, cam.getHeight()-40, 0);
+        shadowFilterText.setLocalTranslation(10, cam.getHeight()-60, 0);
+        shadowIntensityText.setLocalTranslation(10, cam.getHeight()-80, 0);
+        
+        guiNode.attachChild(shadowTypeText);
+        guiNode.attachChild(shadowCompareText);
+        guiNode.attachChild(shadowFilterText);
+        guiNode.attachChild(shadowIntensityText);
+        
+        inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_SPACE));
+        inputManager.addMapping("changeFiltering", new KeyTrigger(KeyInput.KEY_F));
+        inputManager.addMapping("ShadowUp", new KeyTrigger(KeyInput.KEY_T));
+        inputManager.addMapping("ShadowDown", new KeyTrigger(KeyInput.KEY_G));
+        inputManager.addMapping("ThicknessUp", new KeyTrigger(KeyInput.KEY_Y));
+        inputManager.addMapping("ThicknessDown", new KeyTrigger(KeyInput.KEY_H));
+        inputManager.addMapping("toggleHW", new KeyTrigger(KeyInput.KEY_RETURN));
+
+
+        inputManager.addListener(this, "toggleHW", "toggle", "ShadowUp", "ShadowDown", "ThicknessUp", "ThicknessDown", "changeFiltering");
+
+    }
+    
+    int filteringIndex = 0;
+    int renderModeIndex = 0;
+    
+     public void onAction(String name, boolean keyPressed, float tpf) {
+        if (name.equals("toggle") && keyPressed) {
+            renderModeIndex += 1;
+            renderModeIndex %= 3;
+
+            switch (renderModeIndex) {
+                case 0:
+                    viewPort.addProcessor(plsr);
+                    shadowTypeText.setText(TYPE_TEXT+"Processor");
+                    break;
+                case 1:
+                    viewPort.removeProcessor(plsr);
+                    plsf.setEnabled(true);
+                    shadowTypeText.setText(TYPE_TEXT+"Filter");
+                    break;
+                case 2:
+                    plsf.setEnabled(false);
+                    shadowTypeText.setText(TYPE_TEXT+"None");
+                    break;
+            }
+
+
+
+        } else if (name.equals("toggleHW") && keyPressed) {
+            hardwareShadows = !hardwareShadows;
+            plsr.setCompareMode(hardwareShadows ? PointLightShadowRenderer.CompareMode.Hardware : PointLightShadowRenderer.CompareMode.Software);
+            plsf.setCompareMode(hardwareShadows ? PointLightShadowRenderer.CompareMode.Hardware : PointLightShadowRenderer.CompareMode.Software);
+       
+            shadowCompareText.setText(COMPARE_TEXT+(hardwareShadows?"Hardware":"Software"));       
+        }
+
+
+        if (name.equals("changeFiltering") && keyPressed) {
+            filteringIndex = plsr.getFilterMode().ordinal();
+            filteringIndex = (filteringIndex + 1) % PointLightShadowRenderer.FilterMode.values().length;
+            PointLightShadowRenderer.FilterMode m = PointLightShadowRenderer.FilterMode.values()[filteringIndex];
+            plsr.setFilterMode(m);
+            plsf.setFilterMode(m);           
+            shadowFilterText.setText(FILTERING_TEXT+m.toString());      
+        }
+
+     
+        if (name.equals("ShadowUp") && keyPressed) {
+            plsr.setShadowIntensity(plsr.getShadowIntensity() + 0.1f);
+            plsf.setShadowIntensity(plsr.getShadowIntensity() + 0.1f);
+           
+            shadowIntensityText.setText(INTENSITY_TEXT+plsr.getShadowIntensity());        
+        }
+        if (name.equals("ShadowDown") && keyPressed) {
+            plsr.setShadowIntensity(plsr.getShadowIntensity() - 0.1f);
+            plsf.setShadowIntensity(plsr.getShadowIntensity() - 0.1f);
+            shadowIntensityText.setText(INTENSITY_TEXT+plsr.getShadowIntensity());   
+        }
+        if (name.equals("ThicknessUp") && keyPressed) {
+            plsr.setEdgesThickness(plsr.getEdgesThickness() + 1);
+            plsf.setEdgesThickness(plsr.getEdgesThickness() + 1);
+            System.out.println("Shadow thickness : " + plsr.getEdgesThickness());
+        }
+        if (name.equals("ThicknessDown") && keyPressed) {
+            plsr.setEdgesThickness(plsr.getEdgesThickness() - 1);
+            plsf.setEdgesThickness(plsr.getEdgesThickness() - 1);
+            System.out.println("Shadow thickness : " + plsr.getEdgesThickness());
+        }
+        
+     }
+    float time;
+
+    @Override
+    public void simpleUpdate(float tpf) {
+        time += tpf;
+        lightNode.setLocalTranslation(FastMath.cos(time)*0.4f,lightNode.getLocalTranslation().y , FastMath.sin(time)*0.4f);
+
+    }
+ 
+    private BitmapText createText() {
+        BitmapText t = new BitmapText(guiFont, false);
+        t.setSize(guiFont.getCharSet().getRenderedSize()*0.75f);
+        return t;
+    }
+}

二进制
engine/test-data/Models/Test/CornellBox.j3o