diff --git a/.nb-gradle-properties b/.nb-gradle-properties
index eba3578ee..9ca0f2c0a 100644
--- a/.nb-gradle-properties
+++ b/.nb-gradle-properties
@@ -6,4 +6,30 @@
license.txt
jMonkeyEngine
+
+
+ run
+ no
+
+ run
+
+
+
+ -ea
+
+
+
+ run.single
+ no
+
+ ${project}:run
+
+
+ -PmainClass=${selected-class}
+
+
+ -ea
+
+
+
diff --git a/gradle.properties b/gradle.properties
index 00429d65a..e3651bbe4 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -6,7 +6,7 @@ jmeMainVersion = 3.1
jmeVersionTag = snapshot-github
# specify if JavaDoc should be built
-buildJavaDoc = true
+buildJavaDoc = false
# specify if SDK and Native libraries get built
buildSdkProject = true
diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag
new file mode 100644
index 000000000..d68ec28f0
--- /dev/null
+++ b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag
@@ -0,0 +1,243 @@
+#import "Common/ShaderLib/Parallax.glsllib"
+#import "Common/ShaderLib/PBR.glsllib"
+#import "Common/ShaderLib/Lighting.glsllib"
+
+varying vec2 texCoord;
+#ifdef SEPARATE_TEXCOORD
+ varying vec2 texCoord2;
+#endif
+
+varying vec4 Color;
+
+uniform vec4 g_LightData[NB_LIGHTS];
+
+uniform vec3 g_CameraPosition;
+
+uniform float m_Roughness;
+uniform float m_Metallic;
+
+varying vec3 wPosition;
+
+
+#ifdef INDIRECT_LIGHTING
+ uniform sampler2D m_IntegrateBRDF;
+ uniform samplerCube m_PrefEnvMap;
+ uniform samplerCube m_IrradianceMap;
+#endif
+
+#ifdef BASECOLORMAP
+ uniform sampler2D m_BaseColorMap;
+#endif
+#ifdef METALLICMAP
+ uniform sampler2D m_MetallicMap;
+#endif
+#ifdef ROUGHNESSMAP
+ uniform sampler2D m_RoughnessMap;
+#endif
+
+#ifdef EMISSIVE
+ uniform vec4 m_Emissive;
+#endif
+#ifdef EMISSIVEMAP
+ uniform sampler2D m_EmissiveMap;
+#endif
+#if defined(EMISSIVE) || defined(EMISSIVEMAP)
+ uniform float m_EmissivePower;
+ uniform float m_EmissiveIntensity;
+#endif
+
+#ifdef SPECGLOSSPIPELINE
+ uniform sampler2D m_SpecularMap;
+ uniform sampler2D m_GlossMap;
+#endif
+
+#ifdef PARALLAXMAP
+ uniform sampler2D m_ParallaxMap;
+#endif
+#if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP)))
+ uniform float m_ParallaxHeight;
+#endif
+
+#ifdef LIGHTMAP
+ uniform sampler2D m_LightMap;
+#endif
+
+#ifdef NORMALMAP
+ uniform sampler2D m_NormalMap;
+ varying vec3 wTangent;
+ varying vec3 wBinormal;
+#endif
+varying vec3 wNormal;
+
+#ifdef DISCARD_ALPHA
+uniform float m_AlphaDiscardThreshold;
+#endif
+
+void main(){
+ vec2 newTexCoord;
+
+ #if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP)))
+
+ #ifdef STEEP_PARALLAX
+ #ifdef NORMALMAP_PARALLAX
+ //parallax map is stored in the alpha channel of the normal map
+ newTexCoord = steepParallaxOffset(m_NormalMap, vViewDir, texCoord, m_ParallaxHeight);
+ #else
+ //parallax map is a texture
+ newTexCoord = steepParallaxOffset(m_ParallaxMap, vViewDir, texCoord, m_ParallaxHeight);
+ #endif
+ #else
+ #ifdef NORMALMAP_PARALLAX
+ //parallax map is stored in the alpha channel of the normal map
+ newTexCoord = classicParallaxOffset(m_NormalMap, vViewDir, texCoord, m_ParallaxHeight);
+ #else
+ //parallax map is a texture
+ newTexCoord = classicParallaxOffset(m_ParallaxMap, vViewDir, texCoord, m_ParallaxHeight);
+ #endif
+ #endif
+ #else
+ newTexCoord = texCoord;
+ #endif
+
+ #ifdef BASECOLORMAP
+ vec4 albedo = texture2D(m_BaseColorMap, newTexCoord);
+ #else
+ vec4 albedo = Color;
+ #endif
+ #ifdef ROUGHNESSMAP
+ float Roughness = texture2D(m_RoughnessMap, newTexCoord).r * max(m_Roughness,1e-8);
+ #else
+ float Roughness = max(m_Roughness,1e-8);
+ #endif
+ #ifdef METALLICMAP
+ float Metallic = texture2D(m_MetallicMap, newTexCoord).r;
+ #else
+ float Metallic = max(m_Metallic,0.00);
+ #endif
+
+ //Roughness = max(m_Roughness,1e-8);
+ //Metallic = max(m_Metallic,0.00);
+ float alpha = Color.a * albedo.a;
+
+ #ifdef DISCARD_ALPHA
+ if(alpha < m_AlphaDiscardThreshold){
+ discard;
+ }
+ #endif
+
+ // ***********************
+ // Read from textures
+ // ***********************
+ #if defined(NORMALMAP)
+ vec4 normalHeight = texture2D(m_NormalMap, newTexCoord);
+ //Note the -2.0 and -1.0. We invert the green channel of the normal map,
+ //as it's complient with normal maps generated with blender.
+ //see http://hub.jmonkeyengine.org/forum/topic/parallax-mapping-fundamental-bug/#post-256898
+ //for more explanation.
+ vec3 normal = normalize((normalHeight.xyz * vec3(2.0,-2.0,2.0) - vec3(1.0,-1.0,1.0)));
+ #else
+ vec3 normal = normalize(wNormal);
+ #endif
+
+
+ #ifdef LIGHTMAP
+ vec3 lightMapColor;
+ #ifdef SEPARATE_TEXCOORD
+ lightMapColor = texture2D(m_LightMap, texCoord2).rgb;
+ #else
+ lightMapColor = texture2D(m_LightMap, texCoord).rgb;
+ #endif
+ specularColor.rgb *= lightMapColor;
+ albedo.rgb *= lightMapColor;
+ #endif
+
+ int i = 0;
+
+
+ #ifdef NORMALMAP
+ mat3 tbnMat = mat3(normalize(wTangent.xyz) , normalize(wBinormal.xyz) , normalize(wNormal.xyz));
+ normal = normalize(tbnMat * normal);
+// normal = normalize(normal * inverse(tbnMat));
+ #endif
+ vec3 viewDir = normalize(g_CameraPosition - wPosition);
+
+
+ float specular = 0.5;
+
+ #ifdef SPECGLOSSPIPELINE
+ vec4 specularColor = texture2D(m_SpecularMap, newTexCoord);
+ vec4 diffuseColor = albedo;
+ Roughness = 1.0 - texture2D(m_GlossMap, newTexCoord);
+ #else
+ float nonMetalSpec = 0.08 * specular;
+ vec4 specularColor = (nonMetalSpec - nonMetalSpec * Metallic) + albedo * Metallic;
+ vec4 diffuseColor = albedo - albedo * Metallic;
+ #endif
+
+ gl_FragColor.rgb = vec3(0.0);
+ float ndotv = max( dot( normal, viewDir ),0.0);
+ for( int i = 0;i < NB_LIGHTS; i+=3){
+ vec4 lightColor = g_LightData[i];
+ vec4 lightData1 = g_LightData[i+1];
+ vec4 lightDir;
+ vec3 lightVec;
+ lightComputeDir(wPosition, lightColor.w, lightData1, lightDir, lightVec);
+
+ float fallOff = 1.0;
+ #if __VERSION__ >= 110
+ // allow use of control flow
+ if(lightColor.w > 1.0){
+ #endif
+ fallOff = computeSpotFalloff(g_LightData[i+2], lightVec);
+ #if __VERSION__ >= 110
+ }
+ #endif
+ //point light attenuation
+ fallOff *= lightDir.w;
+
+ lightDir.xyz = normalize(lightDir.xyz);
+ vec3 directDiffuse;
+ vec3 directSpecular;
+
+ PBR_ComputeDirectLight(normal, lightDir.xyz, viewDir,
+ lightColor.rgb,specular, Roughness, ndotv,
+ directDiffuse, directSpecular);
+
+ vec3 directLighting = diffuseColor.rgb *directDiffuse + directSpecular * specularColor.rgb;
+
+ gl_FragColor.rgb += directLighting * fallOff;
+ }
+
+ #ifdef INDIRECT_LIGHTING
+ vec3 rv = reflect(-viewDir.xyz, normal.xyz);
+
+ //horizon fade from http://marmosetco.tumblr.com/post/81245981087
+ float horiz = dot(rv, wNormal.xyz);
+ float horizFadePower= 1.0 - Roughness;
+ horiz = clamp( 1.0 + horizFadePower * horiz, 0.0, 1.0 );
+ horiz *= horiz;
+
+ vec3 indirectDiffuse = vec3(0.0);
+ vec3 indirectSpecular = vec3(0.0);
+ indirectDiffuse = textureCube(m_IrradianceMap, rv.xyz).rgb * albedo.rgb;
+
+ indirectSpecular = ApproximateSpecularIBL(m_PrefEnvMap,m_IntegrateBRDF, specularColor.rgb, Roughness, ndotv, rv.xyz);
+ indirectSpecular *= vec3(horiz);
+
+ vec3 indirectLighting = indirectDiffuse + indirectSpecular;
+
+ gl_FragColor.rgb = gl_FragColor.rgb + indirectLighting ;
+ #endif
+
+ #if defined(EMISSIVE) || defined (EMISSIVEMAP)
+ #ifdef EMISSIVEMAP
+ vec4 emissive = texture2D(m_EmissiveMap, newTexCoord);
+ #else
+ vec4 emissive = m_Emissive;
+ #endif
+ gl_FragColor += emissive * pow(emissive.a, m_EmissivePower) * m_EmissiveIntensity;
+ #endif
+
+ gl_FragColor.a = alpha;
+
+}
diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.j3md b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.j3md
new file mode 100644
index 000000000..f2d89cf51
--- /dev/null
+++ b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.j3md
@@ -0,0 +1,305 @@
+MaterialDef PBR Lighting {
+
+ MaterialParameters {
+
+ // Alpha threshold for fragment discarding
+ Float AlphaDiscardThreshold (AlphaTestFallOff)
+
+ //metalness of the material
+ Float Metallic : 0.0
+ //Roughness of the material
+ Float Roughness : 1.0
+ // Base material color
+ Color BaseColor
+ // The emissive color of the object
+ Color Emissive
+ // the emissive power
+ Float EmissivePower : 3.0
+ // the emissive intensity
+ Float EmissiveIntensity : 1.0
+
+ // BaseColor map
+ Texture2D BaseColorMap
+
+ // Specular/gloss map
+ Texture2D MetallicMap -LINEAR
+
+ // Roughness Map
+ Texture2D RoughnessMap -LINEAR
+
+ // Texture of the emissive parts of the material
+ Texture2D EmissiveMap
+
+ // Normal map
+ Texture2D NormalMap -LINEAR
+
+ // For Spec gloss pipeline
+ Texture2D SpecularMap
+ Texture2D GlossMap
+
+ // Prefiltered Env Map for indirect specular lighting
+ TextureCubeMap PrefEnvMap -LINEAR
+
+ // Irradiance map for indirect diffuse lighting
+ TextureCubeMap IrradianceMap -LINEAR
+
+ //integrate BRDF map for indirect Lighting
+ Texture2D IntegrateBRDF -LINEAR
+
+ // Parallax/height map
+ Texture2D ParallaxMap -LINEAR
+
+ //Set to true is parallax map is stored in the alpha channel of the normal map
+ Boolean PackedNormalParallax
+
+ //Sets the relief height for parallax mapping
+ Float ParallaxHeight : 0.05
+
+ //Set to true to activate Steep Parallax mapping
+ Boolean SteepParallax
+
+ // Set to Use Lightmap
+ Texture2D LightMap
+
+ // Set to use TexCoord2 for the lightmap sampling
+ Boolean SeparateTexCoord
+
+ //shadows
+ Int FilterMode
+ Boolean HardwareShadows
+
+ Texture2D ShadowMap0
+ Texture2D ShadowMap1
+ Texture2D ShadowMap2
+ Texture2D ShadowMap3
+ //pointLights
+ Texture2D ShadowMap4
+ Texture2D ShadowMap5
+
+ Float ShadowIntensity
+ Vector4 Splits
+ Vector2 FadeInfo
+
+ Matrix4 LightViewProjectionMatrix0
+ Matrix4 LightViewProjectionMatrix1
+ Matrix4 LightViewProjectionMatrix2
+ Matrix4 LightViewProjectionMatrix3
+ //pointLight
+ Matrix4 LightViewProjectionMatrix4
+ Matrix4 LightViewProjectionMatrix5
+ Vector3 LightPos
+ Vector3 LightDir
+
+ Float PCFEdge
+ Float ShadowMapSize
+
+ // For hardware skinning
+ Int NumberOfBones
+ Matrix4Array BoneMatrices
+
+ //For instancing
+ Boolean UseInstancing
+
+ //For Vertex Color
+ Boolean UseVertexColor
+ }
+
+ Technique {
+ LightMode SinglePass
+
+ VertexShader GLSL100: Common/MatDefs/Light/PBRLighting.vert
+ FragmentShader GLSL100: Common/MatDefs/Light/PBRLighting.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ CameraPosition
+ WorldMatrix
+ }
+
+ Defines {
+ BASECOLORMAP : BaseColorMap
+ NORMALMAP : NormalMap
+ METALLICMAP : MetallicMap
+ ROUGHNESSMAP : RoughnessMap
+ EMISSIVEMAP : EmissiveMap
+ EMISSIVE : Emissive
+ SPECGLOSSPIPELINE : SpecularMap
+ PARALLAXMAP : ParallaxMap
+ NORMALMAP_PARALLAX : PackedNormalParallax
+ STEEP_PARALLAX : SteepParallax
+ LIGHTMAP : LightMap
+ SEPARATE_TEXCOORD : SeparateTexCoord
+ DISCARD_ALPHA : AlphaDiscardThreshold
+ NUM_BONES : NumberOfBones
+ INSTANCING : UseInstancing
+ INDIRECT_LIGHTING : IntegrateBRDF
+ VERTEX_COLOR : UseVertexColor
+ }
+ }
+
+
+
+ Technique PreShadow {
+
+ VertexShader GLSL100 : Common/MatDefs/Shadow/PreShadow.vert
+ FragmentShader GLSL100 : Common/MatDefs/Shadow/PreShadow.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldViewMatrix
+ ViewProjectionMatrix
+ ViewMatrix
+ }
+
+ Defines {
+ COLOR_MAP : ColorMap
+ DISCARD_ALPHA : AlphaDiscardThreshold
+ NUM_BONES : NumberOfBones
+ INSTANCING : UseInstancing
+ }
+
+ ForcedRenderState {
+ FaceCull Off
+ DepthTest On
+ DepthWrite On
+ PolyOffset 5 3
+ ColorWrite Off
+ }
+
+ }
+
+
+ Technique PostShadow15{
+ VertexShader GLSL150: Common/MatDefs/Shadow/PostShadow15.vert
+ FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow15.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldMatrix
+ ViewProjectionMatrix
+ ViewMatrix
+ }
+
+ Defines {
+ HARDWARE_SHADOWS : HardwareShadows
+ FILTER_MODE : FilterMode
+ PCFEDGE : PCFEdge
+ DISCARD_ALPHA : AlphaDiscardThreshold
+ COLOR_MAP : ColorMap
+ SHADOWMAP_SIZE : ShadowMapSize
+ FADE : FadeInfo
+ PSSM : Splits
+ POINTLIGHT : LightViewProjectionMatrix5
+ NUM_BONES : NumberOfBones
+ INSTANCING : UseInstancing
+ }
+
+ ForcedRenderState {
+ Blend Modulate
+ DepthWrite Off
+ PolyOffset -0.1 0
+ }
+ }
+
+ Technique PostShadow{
+ VertexShader GLSL100: Common/MatDefs/Shadow/PostShadow.vert
+ FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadow.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldMatrix
+ ViewProjectionMatrix
+ ViewMatrix
+ }
+
+ Defines {
+ HARDWARE_SHADOWS : HardwareShadows
+ FILTER_MODE : FilterMode
+ PCFEDGE : PCFEdge
+ DISCARD_ALPHA : AlphaDiscardThreshold
+ COLOR_MAP : ColorMap
+ SHADOWMAP_SIZE : ShadowMapSize
+ FADE : FadeInfo
+ PSSM : Splits
+ POINTLIGHT : LightViewProjectionMatrix5
+ NUM_BONES : NumberOfBones
+ INSTANCING : UseInstancing
+ }
+
+ ForcedRenderState {
+ Blend Modulate
+ DepthWrite Off
+ PolyOffset -0.1 0
+ }
+ }
+
+ Technique PreNormalPass {
+
+ VertexShader GLSL100 : Common/MatDefs/SSAO/normal.vert
+ FragmentShader GLSL100 : Common/MatDefs/SSAO/normal.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldViewMatrix
+ NormalMatrix
+ ViewProjectionMatrix
+ ViewMatrix
+ }
+
+ Defines {
+ DIFFUSEMAP_ALPHA : DiffuseMap
+ NUM_BONES : NumberOfBones
+ INSTANCING : UseInstancing
+ }
+
+ }
+
+
+ Technique PreNormalPassDerivative {
+
+ VertexShader GLSL100 : Common/MatDefs/MSSAO/normal.vert
+ FragmentShader GLSL100 : Common/MatDefs/MSSAO/normal.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ WorldViewMatrix
+ NormalMatrix
+ ViewProjectionMatrix
+ ViewMatrix
+ }
+
+ Defines {
+ DIFFUSEMAP_ALPHA : DiffuseMap
+ NUM_BONES : NumberOfBones
+ INSTANCING : UseInstancing
+ }
+
+ }
+
+ Technique GBuf {
+
+ VertexShader GLSL100: Common/MatDefs/Light/GBuf.vert
+ FragmentShader GLSL100: Common/MatDefs/Light/GBuf.frag
+
+ WorldParameters {
+ WorldViewProjectionMatrix
+ NormalMatrix
+ WorldViewMatrix
+ WorldMatrix
+ }
+
+ Defines {
+ VERTEX_COLOR : UseVertexColor
+ MATERIAL_COLORS : UseMaterialColors
+ V_TANGENT : VTangent
+ MINNAERT : Minnaert
+ WARDISO : WardIso
+
+ DIFFUSEMAP : DiffuseMap
+ NORMALMAP : NormalMap
+ SPECULARMAP : SpecularMap
+ PARALLAXMAP : ParallaxMap
+ }
+ }
+
+}
diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.vert b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.vert
new file mode 100644
index 000000000..434d82e1d
--- /dev/null
+++ b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.vert
@@ -0,0 +1,67 @@
+#import "Common/ShaderLib/Instancing.glsllib"
+#import "Common/ShaderLib/Skinning.glsllib"
+
+uniform vec4 m_BaseColor;
+
+uniform vec4 g_AmbientLightColor;
+varying vec2 texCoord;
+
+#ifdef SEPARATE_TEXCOORD
+ varying vec2 texCoord2;
+ attribute vec2 inTexCoord2;
+#endif
+
+varying vec4 Color;
+
+attribute vec3 inPosition;
+attribute vec2 inTexCoord;
+attribute vec3 inNormal;
+
+#ifdef VERTEX_COLOR
+ attribute vec4 inColor;
+#endif
+
+varying vec3 wNormal;
+varying vec3 wPosition;
+#ifdef NORMALMAP
+ attribute vec4 inTangent;
+ varying vec3 wTangent;
+ varying vec3 wBinormal;
+#endif
+
+void main(){
+ vec4 modelSpacePos = vec4(inPosition, 1.0);
+ vec3 modelSpaceNorm = inNormal;
+
+ #if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
+ vec3 modelSpaceTan = inTangent.xyz;
+ #endif
+
+ #ifdef NUM_BONES
+ #if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
+ Skinning_Compute(modelSpacePos, modelSpaceNorm, modelSpaceTan);
+ #else
+ Skinning_Compute(modelSpacePos, modelSpaceNorm);
+ #endif
+ #endif
+
+ gl_Position = TransformWorldViewProjection(modelSpacePos);
+ texCoord = inTexCoord;
+ #ifdef SEPARATE_TEXCOORD
+ texCoord2 = inTexCoord2;
+ #endif
+
+ wPosition = TransformWorld(modelSpacePos).xyz;
+ wNormal = TransformWorld(vec4(modelSpaceNorm,0.0)).xyz;
+
+ #if defined(NORMALMAP)
+ wTangent = TransformWorld(vec4(modelSpaceTan,0.0)).xyz;
+ wBinormal = cross(wNormal, wTangent)* inTangent.w;
+ #endif
+
+ Color = m_BaseColor;
+
+ #ifdef VERTEX_COLOR
+ Color *= inColor;
+ #endif
+}
\ No newline at end of file
diff --git a/jme3-core/src/main/resources/Common/ShaderLib/PBR.glsllib b/jme3-core/src/main/resources/Common/ShaderLib/PBR.glsllib
new file mode 100644
index 000000000..ebb11ac76
--- /dev/null
+++ b/jme3-core/src/main/resources/Common/ShaderLib/PBR.glsllib
@@ -0,0 +1,120 @@
+
+#ifndef PI
+ #define PI 3.14159265358979323846264
+#endif
+
+//Specular fresnel computation
+vec3 F_Shlick(float vh, vec3 F0){
+ float fresnelFact = pow(2.0, (-5.55473*vh - 6.98316) * vh);
+ return mix(F0, vec3(1.0, 1.0, 1.0), fresnelFact);
+}
+
+void PBR_ComputeDirectLightSpecWF(vec3 normal, vec3 lightDir, vec3 viewDir,
+ vec3 lightColor, vec3 specColor, float roughness, float ndotv,
+ out vec3 outDiffuse, out vec3 outSpecular){
+ // Compute halfway vector.
+ vec3 halfVec = normalize(lightDir + viewDir);
+
+ // Compute ndotl, ndoth, vdoth terms which are needed later.
+ float ndotl = max( dot(normal, lightDir), 0.0);
+ float ndoth = max( dot(normal, halfVec), 0.0);
+ float hdotv = max( dot(viewDir, halfVec), 0.0);
+
+ // Compute diffuse using energy-conserving Lambert.
+ // Alternatively, use Oren-Nayar for really rough
+ // materials or if you have lots of processing power ...
+ outDiffuse = vec3(ndotl) * lightColor;
+
+ //cook-torrence, microfacet BRDF : http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf
+
+ float alpha = roughness * roughness;
+
+ //D, GGX normaal Distribution function
+ float alpha2 = alpha * alpha;
+ float sum = ((ndoth * ndoth) * (alpha2 - 1.0) + 1.0);
+ float denom = PI * sum * sum;
+ float D = alpha2 / denom;
+
+ // Compute Fresnel function via Schlick's approximation.
+ vec3 fresnel = F_Shlick(hdotv, specColor);
+
+ //G Shchlick GGX Gometry shadowing term, k = alpha/2
+ float k = alpha * 0.5;
+
+ // UE4 way to optimise shlick GGX Gometry shadowing term
+ //http://graphicrants.blogspot.co.uk/2013/08/specular-brdf-reference.html
+ float G_V = ndotv + sqrt( (ndotv - ndotv * k) * ndotv + k );
+ float G_L = ndotl + sqrt( (ndotl - ndotl * k) * ndotl + k );
+ // the max here is to avoid division by 0 that may cause some small glitches.
+ float G = 1.0/max( G_V * G_L ,0.01);
+
+ float specular = D * G * ndotl;
+
+ outSpecular = fresnel * vec3(specular) * lightColor;
+}
+
+void PBR_ComputeDirectLight(vec3 normal, vec3 lightDir, vec3 viewDir,
+ vec3 lightColor, float fZero, float roughness, float ndotv,
+ out vec3 outDiffuse, out vec3 outSpecular){
+ // Compute halfway vector.
+ vec3 halfVec = normalize(lightDir + viewDir);
+
+ // Compute ndotl, ndoth, vdoth terms which are needed later.
+ float ndotl = max( dot(normal, lightDir), 0.0);
+ float ndoth = max( dot(normal, halfVec), 0.0);
+ float hdotv = max( dot(viewDir, halfVec), 0.0);
+
+ // Compute diffuse using energy-conserving Lambert.
+ // Alternatively, use Oren-Nayar for really rough
+ // materials or if you have lots of processing power ...
+ outDiffuse = vec3(ndotl) * lightColor;
+
+ //cook-torrence, microfacet BRDF : http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf
+
+ float alpha = roughness * roughness;
+
+ //D, GGX normaal Distribution function
+ float alpha2 = alpha * alpha;
+ float sum = ((ndoth * ndoth) * (alpha2 - 1.0) + 1.0);
+ float denom = PI * sum * sum;
+ float D = alpha2 / denom;
+
+ // Compute Fresnel function via Schlick's approximation.
+ float fresnel = fZero + ( 1.0 - fZero ) * pow( 1.0 - hdotv, 5.0 );
+
+ //G Shchlick GGX Gometry shadowing term, k = alpha/2
+ float k = alpha * 0.5;
+
+ /*
+ //classic Schlick ggx
+ float G_V = ndotv / (ndotv * (1.0 - k) + k);
+ float G_L = ndotl / (ndotl * (1.0 - k) + k);
+ float G = ( G_V * G_L );
+
+ float specular =(D* fresnel * G) /(4 * ndotv);
+ */
+
+ // UE4 way to optimise shlick GGX Gometry shadowing term
+ //http://graphicrants.blogspot.co.uk/2013/08/specular-brdf-reference.html
+ float G_V = ndotv + sqrt( (ndotv - ndotv * k) * ndotv + k );
+ float G_L = ndotl + sqrt( (ndotl - ndotl * k) * ndotl + k );
+ // the max here is to avoid division by 0 that may cause some small glitches.
+ float G = 1.0/max( G_V * G_L ,0.01);
+
+ float specular = D * fresnel * G * ndotl;
+
+ outSpecular = vec3(specular) * lightColor;
+}
+
+
+vec3 ApproximateSpecularIBL(samplerCube envMap,sampler2D integrateBRDF, vec3 SpecularColor , float Roughness, float ndotv, vec3 refVec){
+ //TODO magic values should be replaced by defines.
+ float Lod = log2(Roughness) * 1.2 + 6.0 - 1.0;
+ vec3 PrefilteredColor = textureCube(envMap, refVec.xyz,Lod).rgb;
+ vec2 EnvBRDF = texture2D(integrateBRDF,vec2(Roughness, ndotv)).rg;
+ return PrefilteredColor * ( SpecularColor * EnvBRDF.x+ EnvBRDF.y );
+}
+
+
+
+
diff --git a/jme3-core/src/main/resources/Common/Textures/integrateBRDF.ktx b/jme3-core/src/main/resources/Common/Textures/integrateBRDF.ktx
new file mode 100644
index 000000000..2fc2e4690
Binary files /dev/null and b/jme3-core/src/main/resources/Common/Textures/integrateBRDF.ktx differ
diff --git a/jme3-core/src/plugins/java/com/jme3/texture/plugins/DDSLoader.java b/jme3-core/src/plugins/java/com/jme3/texture/plugins/DDSLoader.java
index e7ef0bcd9..245591c05 100644
--- a/jme3-core/src/plugins/java/com/jme3/texture/plugins/DDSLoader.java
+++ b/jme3-core/src/plugins/java/com/jme3/texture/plugins/DDSLoader.java
@@ -296,6 +296,12 @@ public class DDSLoader implements AssetLoader {
// exit here, the rest of the structure is not valid
// the real format will be available in the DX10 header
return;
+
+ case 113:
+ compressed = false;
+ bpp = 64;
+ pixelFormat = Image.Format.RGBA16F;
+ break;
default:
throw new IOException("Unknown fourcc: " + string(fourcc) + ", " + Integer.toHexString(fourcc));
}
diff --git a/jme3-examples/src/main/java/jme3test/batching/TestBatchNodeCluster.java b/jme3-examples/src/main/java/jme3test/batching/TestBatchNodeCluster.java
index 4559acfdc..9006f199d 100644
--- a/jme3-examples/src/main/java/jme3test/batching/TestBatchNodeCluster.java
+++ b/jme3-examples/src/main/java/jme3test/batching/TestBatchNodeCluster.java
@@ -60,7 +60,7 @@ public class TestBatchNodeCluster extends SimpleApplication {
settingst.setVSync(false);
settingst.setFullscreen(false);
app.setSettings(settingst);
- app.setShowSettings(false);
+ app.setShowSettings(false);
app.start();
}
private ActionListener al = new ActionListener() {
diff --git a/jme3-examples/src/main/java/jme3test/light/TestShadowBug.java b/jme3-examples/src/main/java/jme3test/light/TestShadowBug.java
new file mode 100644
index 000000000..cecb53224
--- /dev/null
+++ b/jme3-examples/src/main/java/jme3test/light/TestShadowBug.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2009-2015 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.light.DirectionalLight;
+import com.jme3.light.PointLight;
+import com.jme3.light.SpotLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.queue.RenderQueue.ShadowMode;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.shape.Box;
+import com.jme3.shadow.EdgeFilteringMode;
+import com.jme3.shadow.PointLightShadowRenderer;
+import com.jme3.shadow.SpotLightShadowRenderer;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+
+
+public class TestShadowBug extends SimpleApplication {
+ public static void main(String[] args) {
+ TestShadowBug app = new TestShadowBug();
+ app.start();
+ }
+
+ @Override
+ public void simpleInitApp() {
+ flyCam.setMoveSpeed(100f);
+ rootNode.attachChild(makeFloor());
+
+ Node characters = new Node("Characters");
+ characters.setShadowMode(ShadowMode.Cast);
+ rootNode.attachChild(characters);
+
+ Spatial golem = assetManager.loadModel("Models/Oto/Oto.mesh.xml");
+ golem.scale(0.5f);
+ golem.setLocalTranslation(200.0f, -6f, 200f);
+ golem.setShadowMode(ShadowMode.CastAndReceive);
+ characters.attachChild(golem);
+
+ DirectionalLight sun = new DirectionalLight();
+ sun.setDirection(new Vector3f(-1f, -1f, 1f));
+ sun.setColor(ColorRGBA.White.mult(1.3f));
+ rootNode.addLight(sun);
+ characters.addLight(sun);
+
+ SpotLight spot = new SpotLight();
+ spot.setSpotRange(13f); // distance
+ spot.setSpotInnerAngle(15f * FastMath.DEG_TO_RAD); // inner light cone (central beam)
+ spot.setSpotOuterAngle(20f * FastMath.DEG_TO_RAD); // outer light cone (edge of the light)
+ spot.setColor(ColorRGBA.White.mult(1.3f)); // light color
+ spot.setPosition(new Vector3f(192.0f, -1f, 192f));
+ spot.setDirection(new Vector3f(1, -0.5f, 1));
+ rootNode.addLight(spot);
+
+ PointLight lamp_light = new PointLight();
+ lamp_light.setColor(ColorRGBA.Yellow);
+ lamp_light.setRadius(20f);
+ lamp_light.setPosition(new Vector3f(210.0f, 0f, 210f));
+ rootNode.addLight(lamp_light);
+
+ SpotLightShadowRenderer slsr = new SpotLightShadowRenderer(assetManager, 512);
+ slsr.setLight(spot);
+ slsr.setEdgeFilteringMode(EdgeFilteringMode.Nearest);
+ slsr.setShadowIntensity(0.6f);
+ slsr.setFlushQueues(false);
+ viewPort.addProcessor(slsr);
+
+ PointLightShadowRenderer plsr = new PointLightShadowRenderer(assetManager, 512);
+ plsr.setLight(lamp_light);
+ plsr.setShadowIntensity(0.6f);
+ plsr.setEdgeFilteringMode(EdgeFilteringMode.Nearest);
+ plsr.setFlushQueues(false);
+ viewPort.addProcessor(plsr);
+
+ viewPort.getCamera().setLocation(new Vector3f(192.0f, 10f, 192f));
+ float[] angles = new float[]{3.14f/2, 3.14f/2, 0};
+ viewPort.getCamera().setRotation(new Quaternion(angles));
+ }
+
+ protected Geometry makeFloor() {
+ Box box = new Box(220, .2f, 220);
+ box.scaleTextureCoordinates(new Vector2f(10, 10));
+ Geometry floor = new Geometry("the Floor", box);
+ floor.setLocalTranslation(200, -9, 200);
+ Material matGroundL = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
+ Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
+ grass.setWrap(WrapMode.Repeat);
+ matGroundL.setTexture("DiffuseMap", grass);
+ floor.setMaterial(matGroundL);
+ floor.setShadowMode(ShadowMode.CastAndReceive);
+ return floor;
+ }
+}
\ No newline at end of file
diff --git a/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRLighting.java b/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRLighting.java
new file mode 100644
index 000000000..4c4095f6d
--- /dev/null
+++ b/jme3-examples/src/main/java/jme3test/light/pbr/TestPBRLighting.java
@@ -0,0 +1,172 @@
+package jme3test.light.pbr;
+
+import com.jme3.app.SimpleApplication;
+import com.jme3.input.ChaseCamera;
+import com.jme3.input.KeyInput;
+import com.jme3.input.controls.ActionListener;
+import com.jme3.input.controls.KeyTrigger;
+import com.jme3.light.DirectionalLight;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.post.filters.FXAAFilter;
+import com.jme3.post.filters.ToneMapFilter;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.texture.pbr.EnvironmentCamera;
+import com.jme3.texture.plugins.ktx.KTXLoader;
+import com.jme3.util.SkyFactory;
+
+/**
+ * A test case for PBR lighting.
+ * Still experimental.
+ *
+ * @author nehon
+ */
+public class TestPBRLighting extends SimpleApplication {
+
+ public static void main(String[] args) {
+ TestPBRLighting app = new TestPBRLighting();
+ app.start();
+ }
+ private Geometry model;
+ private DirectionalLight dl;
+ private Node modelNode;
+ private int frame = 0;
+ private boolean indirectLighting = true;
+ private Material pbrMat;
+ private Material adHocMat;
+
+ @Override
+ public void simpleInitApp() {
+ assetManager.registerLoader(KTXLoader.class, "ktx");
+
+ viewPort.setBackgroundColor(ColorRGBA.White);
+ modelNode = (Node) new Node("modelNode");
+ model = (Geometry) assetManager.loadModel("Models/Tank/tank.j3o");
+ modelNode.attachChild(model);
+
+ dl = new DirectionalLight();
+ dl.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
+ rootNode.addLight(dl);
+ dl.setColor(ColorRGBA.White);
+ rootNode.attachChild(modelNode);
+
+ final EnvironmentCamera envCam = new EnvironmentCamera(128, new Vector3f(0, 3f, 0));
+ stateManager.attach(envCam);
+ FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
+ fpp.addFilter(new FXAAFilter());
+ fpp.addFilter(new ToneMapFilter(Vector3f.UNIT_XYZ.mult(2.0f)));
+ viewPort.addProcessor(fpp);
+
+ //Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Sky_Cloudy.hdr", SkyFactory.EnvMapType.EquirectMap);
+ Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Path.hdr", SkyFactory.EnvMapType.EquirectMap);
+ //Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Stonewall.hdr", SkyFactory.EnvMapType.EquirectMap);
+ //Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/road.hdr", SkyFactory.EnvMapType.EquirectMap);
+ rootNode.attachChild(sky);
+
+ pbrMat = assetManager.loadMaterial("Models/Tank/tank.j3m");
+ model.setMaterial(pbrMat);
+
+ ChaseCamera chaser = new ChaseCamera(cam, modelNode, inputManager);
+ chaser.setDragToRotate(true);
+ chaser.setMinVerticalRotation(-FastMath.HALF_PI);
+ chaser.setMaxDistance(1000);
+ chaser.setSmoothMotion(true);
+ chaser.setRotationSensitivity(10);
+ chaser.setZoomSensitivity(5);
+ flyCam.setEnabled(false);
+ //flyCam.setMoveSpeed(100);
+
+ inputManager.addListener(new ActionListener() {
+ @Override
+ public void onAction(String name, boolean isPressed, float tpf) {
+ if (name.equals("toggle") && isPressed) {
+ if (!indirectLighting) {
+ toggleIBL();
+
+ } else {
+ pbrMat.clearParam("IntegrateBRDF");
+ indirectLighting = false;
+ }
+ }
+
+ if (name.equals("switchMats") && isPressed) {
+ if (model.getMaterial() == pbrMat) {
+ model.setMaterial(adHocMat);
+ } else {
+ model.setMaterial(pbrMat);
+ }
+ }
+
+ if (name.equals("debug") && isPressed) {
+ envCam.toggleDebug();
+ }
+
+ if (name.equals("up") && isPressed) {
+ model.move(0, tpf * 100f, 0);
+ }
+
+ if (name.equals("down") && isPressed) {
+ model.move(0, -tpf * 100f, 0);
+ }
+ if (name.equals("left") && isPressed) {
+ model.move(0, 0, tpf * 100f);
+ }
+ if (name.equals("right") && isPressed) {
+ model.move(0, 0, -tpf * 100f);
+ }
+ if (name.equals("light") && isPressed) {
+ dl.setDirection(cam.getDirection().normalize());
+ }
+ }
+ }, "toggle", "light", "up", "down", "left", "right", "debug");
+
+ inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_RETURN));
+ inputManager.addMapping("light", new KeyTrigger(KeyInput.KEY_F));
+ inputManager.addMapping("up", new KeyTrigger(KeyInput.KEY_UP));
+ inputManager.addMapping("down", new KeyTrigger(KeyInput.KEY_DOWN));
+ inputManager.addMapping("left", new KeyTrigger(KeyInput.KEY_LEFT));
+ inputManager.addMapping("right", new KeyTrigger(KeyInput.KEY_RIGHT));
+ inputManager.addMapping("debug", new KeyTrigger(KeyInput.KEY_D));
+
+ }
+
+ private void toggleIBL() {
+ ensurePbrMat();
+ pbrMat.setTexture("IrradianceMap", stateManager.getState(EnvironmentCamera.class).getIrradianceMap());
+ pbrMat.setTexture("PrefEnvMap", stateManager.getState(EnvironmentCamera.class).getPrefilteredEnvMap());
+ pbrMat.setTexture("IntegrateBRDF", assetManager.loadTexture("Common/Textures/integrateBRDF.ktx"));
+ indirectLighting = true;
+ }
+
+ private void ensurePbrMat() {
+ if (model.getMaterial() != pbrMat && model.getMaterial() != adHocMat) {
+ pbrMat = model.getMaterial();
+ }
+ }
+
+ @Override
+ public void simpleUpdate(float tpf) {
+ frame++;
+
+ if (frame == 2) {
+ modelNode.removeFromParent();
+ stateManager.getState(EnvironmentCamera.class).snapshot(rootNode, new Runnable() {
+
+ //this code is ensured to be called in the update loop, the run method is called by the EnvCamera app state in it's update cycle
+ @Override
+ public void run() {
+ toggleIBL();
+ }
+ });
+ }
+ if (frame > 2 && modelNode.getParent() == null) {
+ rootNode.attachChild(modelNode);
+ }
+ }
+
+}
diff --git a/jme3-testdata/src/main/resources/Models/Tank/Tank_Base_Color.png b/jme3-testdata/src/main/resources/Models/Tank/Tank_Base_Color.png
new file mode 100644
index 000000000..c021d42f2
Binary files /dev/null and b/jme3-testdata/src/main/resources/Models/Tank/Tank_Base_Color.png differ
diff --git a/jme3-testdata/src/main/resources/Models/Tank/Tank_Emissive.png b/jme3-testdata/src/main/resources/Models/Tank/Tank_Emissive.png
new file mode 100644
index 000000000..7d7c3e45b
Binary files /dev/null and b/jme3-testdata/src/main/resources/Models/Tank/Tank_Emissive.png differ
diff --git a/jme3-testdata/src/main/resources/Models/Tank/Tank_Metallic.png b/jme3-testdata/src/main/resources/Models/Tank/Tank_Metallic.png
new file mode 100644
index 000000000..de7da8ba2
Binary files /dev/null and b/jme3-testdata/src/main/resources/Models/Tank/Tank_Metallic.png differ
diff --git a/jme3-testdata/src/main/resources/Models/Tank/Tank_Normal.png b/jme3-testdata/src/main/resources/Models/Tank/Tank_Normal.png
new file mode 100644
index 000000000..3bb1a2620
Binary files /dev/null and b/jme3-testdata/src/main/resources/Models/Tank/Tank_Normal.png differ
diff --git a/jme3-testdata/src/main/resources/Models/Tank/Tank_Roughness.png b/jme3-testdata/src/main/resources/Models/Tank/Tank_Roughness.png
new file mode 100644
index 000000000..2717275c3
Binary files /dev/null and b/jme3-testdata/src/main/resources/Models/Tank/Tank_Roughness.png differ
diff --git a/jme3-testdata/src/main/resources/Models/Tank/tank.j3m b/jme3-testdata/src/main/resources/Models/Tank/tank.j3m
new file mode 100644
index 000000000..0bd16814e
--- /dev/null
+++ b/jme3-testdata/src/main/resources/Models/Tank/tank.j3m
@@ -0,0 +1,14 @@
+Material Tank : Common/MatDefs/Light/PBRLighting.j3md {
+ MaterialParameters {
+
+ MetallicMap : Flip Models/Tank/Tank_Metallic.png
+ RoughnessMap : Flip Models/Tank/Tank_Roughness.png
+ NormalMap : Flip Models/Tank/Tank_Normal.png
+ BaseColorMap : Flip Models/Tank/Tank_Base_Color.png
+ EmissiveMap : Flip Models/Tank/Tank_Emissive.png
+ EmissiveIntensity : 2.0
+
+ }
+ AdditionalRenderState {
+ }
+}
diff --git a/jme3-testdata/src/main/resources/Models/Tank/tank.j3o b/jme3-testdata/src/main/resources/Models/Tank/tank.j3o
new file mode 100644
index 000000000..a0d007426
Binary files /dev/null and b/jme3-testdata/src/main/resources/Models/Tank/tank.j3o differ
diff --git a/jme3-testdata/src/main/resources/Models/Tank/tank.j3odata b/jme3-testdata/src/main/resources/Models/Tank/tank.j3odata
new file mode 100644
index 000000000..9e0f13099
--- /dev/null
+++ b/jme3-testdata/src/main/resources/Models/Tank/tank.j3odata
@@ -0,0 +1,3 @@
+#
+#Sat Apr 11 15:27:27 CEST 2015
+ORIGINAL_PATH=Models/Tank/tank.obj
diff --git a/jme3-testdata/src/main/resources/Textures/Sky/Path.hdr b/jme3-testdata/src/main/resources/Textures/Sky/Path.hdr
new file mode 100644
index 000000000..69b878681
Binary files /dev/null and b/jme3-testdata/src/main/resources/Textures/Sky/Path.hdr differ