diff --git a/engine/src/core-data/Common/MatDefs/Light/Lighting.j3md b/engine/src/core-data/Common/MatDefs/Light/Lighting.j3md index 23ec35d13..4b9703533 100644 --- a/engine/src/core-data/Common/MatDefs/Light/Lighting.j3md +++ b/engine/src/core-data/Common/MatDefs/Light/Lighting.j3md @@ -108,6 +108,9 @@ MaterialDef Phong Lighting { Texture2D ShadowMap1 Texture2D ShadowMap2 Texture2D ShadowMap3 + //pointLights + Texture2D ShadowMap4 + Texture2D ShadowMap5 Float ShadowIntensity Vector4 Splits @@ -117,6 +120,10 @@ MaterialDef Phong Lighting { Matrix4 LightViewProjectionMatrix1 Matrix4 LightViewProjectionMatrix2 Matrix4 LightViewProjectionMatrix3 + //pointLight + Matrix4 LightViewProjectionMatrix4 + Matrix4 LightViewProjectionMatrix5 + Vector3 LightPos Float PCFEdge Float ShadowMapSize @@ -209,6 +216,8 @@ MaterialDef Phong Lighting { COLOR_MAP : ColorMap SHADOWMAP_SIZE : ShadowMapSize FADE : FadeInfo + PSSM : Splits + POINTLIGHT : LightViewProjectionMatrix5 } ForcedRenderState { @@ -235,6 +244,8 @@ MaterialDef Phong Lighting { COLOR_MAP : ColorMap SHADOWMAP_SIZE : ShadowMapSize FADE : FadeInfo + PSSM : Splits + POINTLIGHT : LightViewProjectionMatrix5 } ForcedRenderState { diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.frag b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.frag index bb2625983..6111f8979 100644 --- a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.frag +++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.frag @@ -18,6 +18,12 @@ uniform mat4 m_LightViewProjectionMatrix1; uniform mat4 m_LightViewProjectionMatrix2; uniform mat4 m_LightViewProjectionMatrix3; +#ifdef POINTLIGHT + uniform vec3 m_LightPos; + uniform mat4 m_LightViewProjectionMatrix4; + uniform mat4 m_LightViewProjectionMatrix5; +#endif + #ifdef FADE uniform vec2 m_FadeInfo; #endif @@ -47,23 +53,53 @@ void main(){ vec4 projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos; vec4 projCoord2 = biasMat * m_LightViewProjectionMatrix2 * 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; - 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 shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y)); diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.j3md b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.j3md index 711729aef..356d353db 100644 --- a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.j3md +++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter.j3md @@ -8,6 +8,10 @@ MaterialDef Post Shadow { Texture2D ShadowMap1 Texture2D ShadowMap2 Texture2D ShadowMap3 + //pointLights + Texture2D ShadowMap4 + Texture2D ShadowMap5 + Vector3 LightPos Float ShadowIntensity Vector4 Splits @@ -16,7 +20,10 @@ MaterialDef Post Shadow { Matrix4 LightViewProjectionMatrix0 Matrix4 LightViewProjectionMatrix1 Matrix4 LightViewProjectionMatrix2 - Matrix4 LightViewProjectionMatrix3 + Matrix4 LightViewProjectionMatrix3 + //pointLight + Matrix4 LightViewProjectionMatrix4 + Matrix4 LightViewProjectionMatrix5 Float PCFEdge @@ -48,6 +55,8 @@ MaterialDef Post Shadow { PCFEDGE : PCFEdge SHADOWMAP_SIZE : ShadowMapSize FADE : FadeInfo + PSSM : Splits + POINTLIGHT : LightViewProjectionMatrix5 } } @@ -66,6 +75,8 @@ MaterialDef Post Shadow { PCFEDGE : PCFEdge SHADOWMAP_SIZE : ShadowMapSize FADE : FadeInfo + PSSM : Splits + POINTLIGHT : LightViewProjectionMatrix5 } } diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter15.frag b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter15.frag index a331b625a..edae776f7 100644 --- a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter15.frag +++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowFilter15.frag @@ -20,6 +20,12 @@ uniform mat4 m_LightViewProjectionMatrix1; uniform mat4 m_LightViewProjectionMatrix2; uniform mat4 m_LightViewProjectionMatrix3; +#ifdef POINTLIGHT + uniform vec3 m_LightPos; + uniform mat4 m_LightViewProjectionMatrix4; + uniform mat4 m_LightViewProjectionMatrix5; +#endif + #ifdef FADE uniform vec2 m_FadeInfo; #endif @@ -47,22 +53,53 @@ vec4 main_multiSample(in int numSample){ vec4 projCoord1 = biasMat * m_LightViewProjectionMatrix1 * worldPos; vec4 projCoord2 = biasMat * m_LightViewProjectionMatrix2 * 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; - 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 shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y)); diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.frag b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.frag index e9661e510..82097b93f 100644 --- a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.frag +++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.frag @@ -1,11 +1,21 @@ #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; @@ -35,19 +45,46 @@ void main(){ #endif 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 shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y)); #endif diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.j3md b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.j3md index f502aaed1..60bf7e3c6 100644 --- a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.j3md +++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.j3md @@ -8,6 +8,9 @@ MaterialDef Post Shadow { Texture2D ShadowMap1 Texture2D ShadowMap2 Texture2D ShadowMap3 + //pointLights + Texture2D ShadowMap4 + Texture2D ShadowMap5 Float ShadowIntensity Vector4 Splits @@ -17,6 +20,10 @@ MaterialDef Post Shadow { Matrix4 LightViewProjectionMatrix1 Matrix4 LightViewProjectionMatrix2 Matrix4 LightViewProjectionMatrix3 + //pointLight + Matrix4 LightViewProjectionMatrix4 + Matrix4 LightViewProjectionMatrix5 + Vector3 LightPos Float PCFEdge @@ -38,6 +45,8 @@ MaterialDef Post Shadow { PCFEDGE : PCFEdge SHADOWMAP_SIZE : ShadowMapSize FADE : FadeInfo + PSSM : Splits + POINTLIGHT : LightViewProjectionMatrix5 } RenderState { @@ -62,6 +71,8 @@ MaterialDef Post Shadow { PCFEDGE : PCFEdge SHADOWMAP_SIZE : ShadowMapSize FADE : FadeInfo + PSSM : Splits + POINTLIGHT : LightViewProjectionMatrix5 } RenderState { diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.vert b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.vert index 2bdfb25b6..2a67c5e2b 100644 --- a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.vert +++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM.vert @@ -11,7 +11,17 @@ 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; @@ -30,9 +40,12 @@ const mat4 biasMat = mat4(0.5, 0.0, 0.0, 0.0, void main(){ 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 - vec4 worldPos = g_WorldMatrix * vec4(inPosition, 1.0); + worldPos = g_WorldMatrix * vec4(inPosition, 1.0); #ifdef DISCARD_ALPHA texCoord = inTexCoord; @@ -42,4 +55,8 @@ void main(){ 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 } \ No newline at end of file diff --git a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM15.frag b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM15.frag index f9cdc5eb4..b97bea408 100644 --- a/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM15.frag +++ b/engine/src/core-data/Common/MatDefs/Shadow/PostShadowPSSM15.frag @@ -1,12 +1,23 @@ #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; @@ -35,20 +46,53 @@ void main(){ } #endif - 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 shadow = max(0.0,mix(shadow,1.0,(shadowPosition - m_FadeInfo.x) * m_FadeInfo.y)); diff --git a/engine/src/core-data/Common/ShaderLib/PssmShadows.glsllib b/engine/src/core-data/Common/ShaderLib/PssmShadows.glsllib index 1e53d86f3..9ead707ab 100644 --- a/engine/src/core-data/Common/ShaderLib/PssmShadows.glsllib +++ b/engine/src/core-data/Common/ShaderLib/PssmShadows.glsllib @@ -3,7 +3,7 @@ #define SHADOWCOMPARE(tex,coord) shadow2DProj(tex, coord).r #else #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 #if FILTER_MODE == 0 @@ -35,8 +35,14 @@ uniform SHADOWMAP m_ShadowMap0; uniform SHADOWMAP m_ShadowMap1; uniform SHADOWMAP m_ShadowMap2; uniform SHADOWMAP m_ShadowMap3; +#ifdef POINTLIGHT +uniform SHADOWMAP m_ShadowMap4; +uniform SHADOWMAP m_ShadowMap5; +#endif +#ifdef PSSM uniform vec4 m_Splits; +#endif uniform float m_ShadowIntensity; @@ -53,10 +59,16 @@ float Shadow_DoShadowCompare(in SHADOWMAP tex, vec4 projCoord){ } 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 } float Shadow_DoDither_2x2(in SHADOWMAP tex, in vec4 projCoord){ diff --git a/engine/src/core-data/Common/ShaderLib/PssmShadows15.glsllib b/engine/src/core-data/Common/ShaderLib/PssmShadows15.glsllib index f52213003..6dd071229 100644 --- a/engine/src/core-data/Common/ShaderLib/PssmShadows15.glsllib +++ b/engine/src/core-data/Common/ShaderLib/PssmShadows15.glsllib @@ -9,8 +9,8 @@ #define SHADOWGATHER(tex,coord) textureGather(tex, coord.xy, coord.z) #else #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)) #endif @@ -45,18 +45,30 @@ uniform SHADOWMAP m_ShadowMap0; uniform SHADOWMAP m_ShadowMap1; uniform SHADOWMAP m_ShadowMap2; uniform SHADOWMAP m_ShadowMap3; +#ifdef POINTLIGHT +uniform SHADOWMAP m_ShadowMap4; +uniform SHADOWMAP m_ShadowMap5; +#endif +#ifdef PSSM uniform vec4 m_Splits; +#endif uniform float m_ShadowIntensity; 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 } 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.z = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(0, 1)); gather.w = SHADOWCOMPAREOFFSET(tex, projCoord, ivec2(1, 1)); - #endif + #endif vec2 f = fract( projCoord.xy * SHADOWMAP_SIZE ); vec2 mx = mix( gather.xz, gather.yw, f.x ); diff --git a/engine/src/core/com/jme3/shadow/PointLightShadowFilter.java b/engine/src/core/com/jme3/shadow/PointLightShadowFilter.java new file mode 100644 index 000000000..966d03726 --- /dev/null +++ b/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 http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html + * @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
+ * @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); + + } +} diff --git a/engine/src/core/com/jme3/shadow/PointLightShadowRenderer.java b/engine/src/core/com/jme3/shadow/PointLightShadowRenderer.java new file mode 100644 index 000000000..331b7d9e8 --- /dev/null +++ b/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)
+ * It splits the view frustum in several parts and compute a shadow map for each + * one.
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.
+ * This result in a better quality shadow than standard shadow mapping.
for + * more informations on this read this http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html
+ *

+ * @author Rémy Bouquet aka Nehon + */ +public class PointLightShadowRenderer implements SceneProcessor { + + /** + * FilterMode 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 matCache = new ArrayList(); + //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 http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html + * + * @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 http://http.developer.nvidia.com/GPUGems3/gpugems3_ch10.html + * + * @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(); + } +} diff --git a/engine/src/core/com/jme3/shadow/ShadowUtil.java b/engine/src/core/com/jme3/shadow/ShadowUtil.java index e9e23a11e..480b2ba05 100644 --- a/engine/src/core/com/jme3/shadow/ShadowUtil.java +++ b/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.min; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; /** @@ -65,19 +66,17 @@ public class ShadowUtil { */ public static void updateFrustumPoints2(Camera viewCam, Vector3f[] points) { 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); } + + /** + * 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); + } + + } } diff --git a/engine/src/test/jme3test/light/TestPointLightShadows.java b/engine/src/test/jme3test/light/TestPointLightShadows.java new file mode 100644 index 000000000..57aec17e6 --- /dev/null +++ b/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; + } +} \ No newline at end of file diff --git a/engine/test-data/Models/Test/CornellBox.j3o b/engine/test-data/Models/Test/CornellBox.j3o new file mode 100644 index 000000000..1d56eefa9 Binary files /dev/null and b/engine/test-data/Models/Test/CornellBox.j3o differ