Compare commits

...

56 Commits

Author SHA1 Message Date
Nehon dd2626c560 Properly remove static pass 7 years ago
Nehon d444c28183 Changes the name of the array shadow renderer to InPassShadowRenderer. Also addressed some comments on the PR 7 years ago
Nehon 54ef6ec280 Merge master into branch 7 years ago
Kirill Vainer cbf6ffaad8 in-pass-shadows: show cfg dialog for test 7 years ago
Kirill Vainer 251511ee00 in-pass-shadows: add customized light sort 7 years ago
Kirill Vainer 2e9996d498 in-pass-shadows: add test example 7 years ago
Kirill Vainer 5f66eeacb4 in-pass-shadows: use glsllib extension 7 years ago
Kirill Vainer 5b800952f0 in-pass-shadows: fix syntax errors 7 years ago
Kirill Vainer caad16626e in-pass-shadows: add spot / point light support 7 years ago
Kirill Vainer 6fb2d029d2 Merge remote-tracking branch 'origin/master' into in-pass-shadows 7 years ago
Kirill Vainer 5108f52ebf Merge remote-tracking branch 'origin/master' into in-pass-shadows 7 years ago
Kirill Vainer 42432ed4ea remove hacks from ShadowUtil 7 years ago
Kirill Vainer 6487def9d3 update GLTracer 7 years ago
Kirill Vainer e4536808ca add point light mode for pre shadow techniques 7 years ago
Kirill Vainer fe158e7b31 fix infinite recursive loop 7 years ago
Kirill Vainer 628fa23059 remove useless normalization in lighting shaders 7 years ago
Kirill Vainer fca6d4a8b2 Merge remote-tracking branch 'origin/master' into in-pass-shadows 7 years ago
Kirill Vainer 8d125a30ba Merge remote-tracking branch 'origin/master' into in-pass-shadows 7 years ago
Kirill Vainer d50fb09efb Merge remote-tracking branch 'origin/master' into in-pass-shadows 7 years ago
Kirill Vainer 59c85d58c8 Merge remote-tracking branch 'origin/master' into in-pass-shadows 7 years ago
Kirill Vainer 4b4bf24127 fix shadow disappearing when frustum in front of caster 7 years ago
Kirill Vainer 0fae3839d3 remove unused methods 7 years ago
Kirill Vainer c3cfab65c6 use 16-bit depth by default 7 years ago
Kirill Vainer c136a4212e use PBR + support point light shadows in shader 7 years ago
Kirill Vainer ec0fcd24d2 support point light shadows 7 years ago
Kirill Vainer 47b34c6de5 optimize clearing shadow maps 7 years ago
Kirill Vainer 1e861fd2fa Merge branch 'in-pass-shadows' of github.com:jMonkeyEngine/jmonkeyengine into in-pass-shadows 7 years ago
Kirill Vainer b52d0e3743 fix wrong counts 7 years ago
Kirill Vainer 3889cb47b7 calculate lighting in world space 7 years ago
Kirill Vainer 2c385914c6 optimize pssm split calculation 7 years ago
Kirill Vainer 0a4a439745 optimize pssm split calculation 7 years ago
Kirill Vainer a3145885d9 Support dynamic number of shadow maps 7 years ago
Kirill Vainer 5aa2c722fe Fix syntax error 7 years ago
Kirill Vainer 77e552f551 Implement static pass in the lighting material 7 years ago
Kirill Vainer 9c4fcac876 Add PreShadowArrayRenderer 7 years ago
Kirill Vainer d159e1746c Combine forced render state with the one in the technique 7 years ago
Kirill Vainer 8a747276d7 Make sure apply = false for all forced render states 7 years ago
Kirill Vainer ce28e35393 Fix additional render state missing apply = false 7 years ago
Kirill Vainer 42051b045b StaticPass supports setting shadow map index on light 7 years ago
Kirill Vainer cfcec44b9a Add ShadowMap interface 7 years ago
Kirill Vainer 55e9fd067a Remove scale from updateFrustumPoints since its always 1.0 7 years ago
Kirill Vainer f5ad0274b3 Make tech logic impls responsible for light filtering 7 years ago
Kirill Vainer 2ce2995956 Don't reset lights that were never checked in light filter 7 years ago
Kirill Vainer 4d60b2df70 Merge remote-tracking branch 'origin/opengles2-fixes' into in-pass-shadows 7 years ago
Kirill Vainer 16e472678a Support shadow maps in GLES 7 years ago
Kirill Vainer bc50b09bf4 Don't upload NULL texture slices 7 years ago
Kirill Vainer 134c3651c8 Merge remote-tracking branch 'origin/master' into opengles2-fixes 7 years ago
Kirill Vainer 7441865307 Print the object with the problem in NativeObjectManager 7 years ago
Kirill Vainer 88aaa079e3 Use the same depth format in all places in FPP 7 years ago
Kirill Vainer b0316e419c Avoid RGB111110F in filters unless its actually supported 7 years ago
Kirill Vainer af3a0c70ce Move glFramebufferTextureLayer to GLFbo 7 years ago
Kirill Vainer 7a22f8c940 Support ABGR8 using swizzle extension 7 years ago
Kirill Vainer 23700d5140 Don't use unsupported TEXTURE_{BASE,MAX}_LEVEL in GLES 7 years ago
Kirill Vainer 69139a1e95 Support instancing in GLTracer 7 years ago
Kirill Vainer 259694605e Support GLTracer and GL debug in Android 7 years ago
Kirill Vainer 406c3144d8 Use correct HALF_FLOAT constant for GLES 7 years ago
  1. 1
      jme3-core/src/main/java/com/jme3/light/DefaultLightFilter.java
  2. 19
      jme3-core/src/main/java/com/jme3/light/Light.java
  3. 14
      jme3-core/src/main/java/com/jme3/light/LightList.java
  4. 62
      jme3-core/src/main/java/com/jme3/material/Material.java
  5. 3
      jme3-core/src/main/java/com/jme3/material/RenderState.java
  6. 11
      jme3-core/src/main/java/com/jme3/material/Technique.java
  7. 11
      jme3-core/src/main/java/com/jme3/material/TechniqueDef.java
  8. 48
      jme3-core/src/main/java/com/jme3/material/logic/DefaultTechniqueDefLogic.java
  9. 8
      jme3-core/src/main/java/com/jme3/material/logic/MultiPassLightingLogic.java
  10. 254
      jme3-core/src/main/java/com/jme3/material/logic/SinglePassAndImageBasedLightingLogic.java
  11. 35
      jme3-core/src/main/java/com/jme3/material/logic/SinglePassLightingLogic.java
  12. 182
      jme3-core/src/main/java/com/jme3/material/logic/StaticPassLightingLogic.java
  13. 17
      jme3-core/src/main/java/com/jme3/material/logic/TechniqueDefLogic.java
  14. 1
      jme3-core/src/main/java/com/jme3/math/Matrix4f.java
  15. 2
      jme3-core/src/main/java/com/jme3/post/PreDepthProcessor.java
  16. 28
      jme3-core/src/main/java/com/jme3/renderer/RenderManager.java
  17. 2
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugES.java
  18. 2
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLTracer.java
  19. 4
      jme3-core/src/main/java/com/jme3/renderer/opengl/TextureUtil.java
  20. 42
      jme3-core/src/main/java/com/jme3/shader/Uniform.java
  21. 2
      jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java
  22. 1
      jme3-core/src/main/java/com/jme3/shadow/BasicShadowRenderer.java
  23. 4
      jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java
  24. 4
      jme3-core/src/main/java/com/jme3/shadow/PssmShadowRenderer.java
  25. 11
      jme3-core/src/main/java/com/jme3/shadow/PssmShadowUtil.java
  26. 275
      jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java
  27. 2
      jme3-core/src/main/java/com/jme3/shadow/SpotLightShadowRenderer.java
  28. 275
      jme3-core/src/main/java/com/jme3/shadow/next/InPassShadowRenderer.java
  29. 183
      jme3-core/src/main/java/com/jme3/shadow/next/PreShadowRenderer.java
  30. 98
      jme3-core/src/main/java/com/jme3/shadow/next/ShadowDebugControl.java
  31. 50
      jme3-core/src/main/java/com/jme3/shadow/next/ShadowMap.java
  32. 58
      jme3-core/src/main/java/com/jme3/shadow/next/ShadowMapSlice.java
  33. 47
      jme3-core/src/main/java/com/jme3/shadow/next/array/ArrayShadowMap.java
  34. 41
      jme3-core/src/main/java/com/jme3/shadow/next/array/ArrayShadowMapSlice.java
  35. 75
      jme3-core/src/main/java/com/jme3/shadow/next/array/BaseArrayShadowMap.java
  36. 98
      jme3-core/src/main/java/com/jme3/shadow/next/array/BaseArrayShadowMapSlice.java
  37. 81
      jme3-core/src/main/java/com/jme3/shadow/next/array/DirectionalArrayShadowMap.java
  38. 75
      jme3-core/src/main/java/com/jme3/shadow/next/array/DirectionalArrayShadowMapSlice.java
  39. 99
      jme3-core/src/main/java/com/jme3/shadow/next/array/PointArrayShadowMap.java
  40. 60
      jme3-core/src/main/java/com/jme3/shadow/next/array/PointArrayShadowMapSlice.java
  41. 66
      jme3-core/src/main/java/com/jme3/shadow/next/array/SpotArrayShadowMap.java
  42. 71
      jme3-core/src/main/java/com/jme3/shadow/next/array/SpotArrayShadowMapSlice.java
  43. 87
      jme3-core/src/main/java/com/jme3/shadow/next/pssm/BaseShadowMapSlice.java
  44. 92
      jme3-core/src/main/java/com/jme3/shadow/next/pssm/DirectionalShadowMap.java
  45. 67
      jme3-core/src/main/java/com/jme3/shadow/next/pssm/DirectionalShadowMapSlice.java
  46. 139
      jme3-core/src/main/java/com/jme3/shadow/next/pssm/DirectionalShadowParameters.java
  47. 2
      jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.frag
  48. 24
      jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.j3md
  49. 2
      jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.vert
  50. 28
      jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag
  51. 7
      jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.j3md
  52. 7
      jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.vert
  53. 2
      jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.frag
  54. 2
      jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.vert
  55. 7
      jme3-core/src/main/resources/Common/MatDefs/Misc/Particle.j3md
  56. 7
      jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.j3md
  57. 13
      jme3-core/src/main/resources/Common/MatDefs/Shadow/PreShadow.j3md
  58. 38
      jme3-core/src/main/resources/Common/MatDefs/Shadow/PreShadow.vert
  59. 15
      jme3-core/src/main/resources/Common/MatDefs/Shadow/ShowShadowArray.frag
  60. 17
      jme3-core/src/main/resources/Common/MatDefs/Shadow/ShowShadowArray.j3md
  61. 1
      jme3-core/src/main/resources/Common/ShaderLib/GLSLCompat.glsllib
  62. 165
      jme3-core/src/main/resources/Common/ShaderLib/InPassShadows.glsllib
  63. 8
      jme3-core/src/main/resources/Common/ShaderLib/Lighting.glsllib
  64. 4
      jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java
  65. 3
      jme3-core/src/test/java/com/jme3/material/MaterialMatParamTest.java
  66. 206
      jme3-examples/src/main/java/jme3test/light/TestInPassShadows.java
  67. 2
      jme3-niftygui/src/main/java/com/jme3/niftygui/JmeBatchRenderBackend.java
  68. 2
      jme3-niftygui/src/main/java/com/jme3/niftygui/RenderDeviceJme.java
  69. 4
      jme3-vr/src/main/java/com/jme3/shadow/DirectionalLightShadowRendererVR.java

@ -59,6 +59,7 @@ public final class DefaultLightFilter implements LightFilter {
for (Light light : processedLights) {
light.frustumCheckNeeded = true;
}
processedLights.clear();
}
@Override

@ -37,6 +37,7 @@ import com.jme3.export.*;
import com.jme3.math.ColorRGBA;
import com.jme3.renderer.Camera;
import com.jme3.scene.Spatial;
import com.jme3.shadow.next.ShadowMap;
import com.jme3.util.TempVars;
import java.io.IOException;
@ -120,6 +121,8 @@ public abstract class Light implements Savable, Cloneable {
boolean frustumCheckNeeded = true;
boolean intersectsFrustum = false;
protected ShadowMap shadowMap;
protected Light() {
}
@ -163,6 +166,22 @@ public abstract class Light implements Savable, Cloneable {
return lastDistance;
}
*/
/**
* @return the light's shadow map, or null if none was assigned.
*/
public ShadowMap getShadowMap() {
return shadowMap;
}
/**
* Used internally to associate the light with a shadow map
*
* @param shadowMap the light's shadow map
*/
public void setShadowMap(ShadowMap shadowMap) {
this.shadowMap = shadowMap;
}
/**
* Sets the light color.

@ -186,7 +186,7 @@ public final class LightList implements Iterable<Light>, Savable, Cloneable, Jme
listSize = 0;
}
/**
* Sorts the elements in the list according to their Comparator.
* There are two reasons why lights should be resorted.
@ -219,6 +219,18 @@ public final class LightList implements Iterable<Light>, Savable, Cloneable, Jme
}
}
public void sort(Comparator<Light> comparator) {
if (listSize > 1) {
if (tlist == null || tlist.length != list.length) {
tlist = list.clone();
} else {
System.arraycopy(list, 0, tlist, 0, list.length);
}
SortUtil.msort(tlist, list, 0, listSize - 1, comparator);
}
}
/**
* Updates a "world-space" light list, using the spatial's local-space
* light list and its parent's world-space light list.

@ -78,9 +78,9 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
private AssetKey key;
private String name;
private MaterialDef def;
private ListMap<String, MatParam> paramValues = new ListMap<String, MatParam>();
private ListMap<String, MatParam> paramValues = new ListMap<>();
private Technique technique;
private HashMap<String, Technique> techniques = new HashMap<String, Technique>();
private HashMap<String, Technique> techniques = new HashMap<>();
private RenderState additionalState = null;
private RenderState mergedRenderState = new RenderState();
private boolean transparent = false;
@ -140,10 +140,12 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
this.name = name;
}
@Override
public void setKey(AssetKey key) {
this.key = key;
}
@Override
public AssetKey getKey() {
return key;
}
@ -197,9 +199,9 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
mat.additionalState = additionalState.clone();
}
mat.technique = null;
mat.techniques = new HashMap<String, Technique>();
mat.techniques = new HashMap<>();
mat.paramValues = new ListMap<String, MatParam>();
mat.paramValues = new ListMap<>();
for (int i = 0; i < paramValues.size(); i++) {
Map.Entry<String, MatParam> entry = paramValues.getEntry(i);
mat.paramValues.put(entry.getKey(), entry.getValue().clone());
@ -866,7 +868,14 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
private void updateRenderState(RenderManager renderManager, Renderer renderer, TechniqueDef techniqueDef) {
if (renderManager.getForcedRenderState() != null) {
renderer.applyRenderState(renderManager.getForcedRenderState());
if (techniqueDef.getForcedRenderState() != null) {
renderer.applyRenderState(
techniqueDef.getForcedRenderState().copyMergedTo(
renderManager.getForcedRenderState(),
mergedRenderState));
} else {
renderer.applyRenderState(renderManager.getForcedRenderState());
}
} else {
if (techniqueDef.getRenderState() != null) {
renderer.applyRenderState(techniqueDef.getRenderState().copyMergedTo(additionalState, mergedRenderState));
@ -899,8 +908,8 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
// Get world overrides
SafeArrayList<MatParamOverride> overrides = geometry.getWorldMatParamOverrides();
Shader shader = technique.makeCurrent(renderManager, overrides, null, null, rendererCaps);
updateShaderMaterialParameters(renderer, shader, overrides, null);
Shader shader = technique.makeCurrent(renderManager, geometry, overrides, renderManager.getForcedMatParams(), rendererCaps);
updateShaderMaterialParameters(renderer, shader, overrides, renderManager.getForcedMatParams());
renderManager.getRenderer().setShader(shader);
}
@ -984,10 +993,9 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
* </ul>
*
* @param geometry The geometry to render
* @param lights Presorted and filtered light list to use for rendering
* @param renderManager The render manager requesting the rendering
*/
public void render(Geometry geometry, LightList lights, RenderManager renderManager) {
public void render(Geometry geometry, RenderManager renderManager) {
if (technique == null) {
selectTechnique(TechniqueDef.DEFAULT_TECHNIQUE_NAME, renderManager);
}
@ -1007,7 +1015,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
SafeArrayList<MatParamOverride> overrides = geometry.getWorldMatParamOverrides();
// Select shader to use
Shader shader = technique.makeCurrent(renderManager, overrides, renderManager.getForcedMatParams(), lights, rendererCaps);
Shader shader = technique.makeCurrent(renderManager, geometry, overrides, renderManager.getForcedMatParams(), rendererCaps);
// Begin tracking which uniforms were changed by material.
clearUniformsSetByCurrent(shader);
@ -1016,29 +1024,24 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
renderManager.updateUniformBindings(shader);
// Set material parameters
int unit = updateShaderMaterialParameters(renderer, shader, overrides, renderManager.getForcedMatParams());
int nextTextureUnit = updateShaderMaterialParameters(renderer, shader, overrides, renderManager.getForcedMatParams());
// Clear any uniforms not changed by material.
resetUniformsNotSetByCurrent(shader);
// Delegate rendering to the technique
technique.render(renderManager, shader, geometry, lights, unit);
technique.render(renderManager, shader, geometry, nextTextureUnit);
}
/**
* Called by {@link RenderManager} to render the geometry by
* using this material.
*
* Note that this version of the render method
* does not perform light filtering.
*
* @param geom The geometry to render
* @param rm The render manager requesting the rendering
*/
public void render(Geometry geom, RenderManager rm) {
render(geom, geom.getWorldLightList(), rm);
@Override
public String toString() {
return "Material[name=" + name +
", def=" + (def != null ? def.getName() : null) +
", tech=" + (technique != null && technique.getDef() != null ? technique.getDef().getName() : null) +
"]";
}
@Override
public void write(JmeExporter ex) throws IOException {
OutputCapsule oc = ex.getCapsule(this);
oc.write(def.getAssetName(), "material_def", null);
@ -1049,13 +1052,6 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
}
@Override
public String toString() {
return "Material[name=" + name +
", def=" + (def != null ? def.getName() : null) +
", tech=" + (technique != null && technique.getDef() != null ? technique.getDef().getName() : null) +
"]";
}
public void read(JmeImporter im) throws IOException {
InputCapsule ic = im.getCapsule(this);
@ -1105,7 +1101,7 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable {
}
def = (MaterialDef) im.getAssetManager().loadAsset(new AssetKey(defName));
paramValues = new ListMap<String, MatParam>();
paramValues = new ListMap<>();
// load the textures and update nextTexUnit
for (Map.Entry<String, MatParam> entry : params.entrySet()) {

@ -435,6 +435,9 @@ public class RenderState implements Cloneable, Savable {
ADDITIONAL.applyColorWrite = false;
ADDITIONAL.applyBlendMode = false;
ADDITIONAL.applyPolyOffset = false;
ADDITIONAL.applyStencilTest = false;
ADDITIONAL.applyLineWidth = false;
ADDITIONAL.applyDepthFunc = false;
}
boolean wireframe = false;
boolean applyWireFrame = true;

@ -133,9 +133,10 @@ public final class Technique {
* @param rendererCaps The renderer capabilities which the shader should support.
* @return A compatible shader.
*/
Shader makeCurrent(RenderManager renderManager, SafeArrayList<MatParamOverride> worldOverrides,
Shader makeCurrent(RenderManager renderManager, Geometry geometry,
SafeArrayList<MatParamOverride> worldOverrides,
SafeArrayList<MatParamOverride> forcedOverrides,
LightList lights, EnumSet<Caps> rendererCaps) {
EnumSet<Caps> rendererCaps) {
TechniqueDefLogic logic = def.getLogic();
AssetManager assetManager = owner.getMaterialDef().getAssetManager();
@ -149,7 +150,7 @@ public final class Technique {
applyOverrides(dynamicDefines, forcedOverrides);
}
return logic.makeCurrent(assetManager, renderManager, rendererCaps, lights, dynamicDefines);
return logic.makeCurrent(assetManager, renderManager, rendererCaps, geometry, dynamicDefines);
}
/**
@ -161,9 +162,9 @@ public final class Technique {
* @param geometry The geometry to render
* @param lights Lights which influence the geometry.
*/
void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights, int lastTexUnit) {
void render(RenderManager renderManager, Shader shader, Geometry geometry, int lastTextureUnit) {
TechniqueDefLogic logic = def.getLogic();
logic.render(renderManager, shader, geometry, lights, lastTexUnit);
logic.render(renderManager, shader, geometry, lastTextureUnit);
}
/**

@ -105,17 +105,6 @@ public class TechniqueDef implements Savable, Cloneable {
*/
@Deprecated
FixedPipeline,
/**
* Similar to {@link #SinglePass} except the type of each light is known
* at shader compile time.
* <p>
* The advantage is that the shader can be much more efficient, i.e. not
* do operations required for spot and point lights if it knows the
* light is a directional light. The disadvantage is that the number of
* shaders used balloons because of the variations in the number of
* lights used by objects.
*/
StaticPass
}
public enum ShadowMode {

@ -43,11 +43,13 @@ import com.jme3.scene.Mesh;
import com.jme3.scene.instancing.InstancedGeometry;
import com.jme3.shader.DefineList;
import com.jme3.shader.Shader;
import com.jme3.shadow.next.array.ArrayShadowMap;
import java.util.EnumSet;
public class DefaultTechniqueDefLogic implements TechniqueDefLogic {
protected final TechniqueDef techniqueDef;
protected final LightList filteredLightList = new LightList(null);
public DefaultTechniqueDefLogic(TechniqueDef techniqueDef) {
this.techniqueDef = techniqueDef;
@ -55,7 +57,7 @@ public class DefaultTechniqueDefLogic implements TechniqueDefLogic {
@Override
public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager,
EnumSet<Caps> rendererCaps, LightList lights, DefineList defines) {
EnumSet<Caps> rendererCaps, Geometry geometry, DefineList defines) {
return techniqueDef.getShader(assetManager, rendererCaps, defines);
}
@ -70,6 +72,41 @@ public class DefaultTechniqueDefLogic implements TechniqueDefLogic {
renderer.renderMesh(mesh, lodLevel, 1, null);
}
}
@Override
public void render(RenderManager renderManager, Shader shader, Geometry geometry, int lastTexUnit) {
Renderer renderer = renderManager.getRenderer();
renderer.setShader(shader);
renderMeshFromGeometry(renderer, geometry);
}
protected LightList getFilteredLightList(RenderManager renderManager, Geometry geom) {
filteredLightList.clear();
renderManager.getLightFilter().filterLights(geom, filteredLightList);
return filteredLightList;
}
protected float encodeLightType(Light light) {
switch (light.getType()) {
case Directional:
return 0.125f;
case Point:
return 0.25f;
case Spot:
return 0.5f;
default:
throw new UnsupportedOperationException("Invalid light type: " + light.getType());
}
}
protected float encodeLightTypeAndShadowMapIndex(Light light) {
if (light.getShadowMap() == null) {
return encodeLightType(light);
} else {
ArrayShadowMap map = (ArrayShadowMap) light.getShadowMap();
return -(encodeLightType(light) + map.getFirstArraySlice());
}
}
protected static ColorRGBA getAmbientColor(LightList lightList, boolean removeLights, ColorRGBA ambientLightColor) {
ambientLightColor.set(0, 0, 0, 1);
@ -85,13 +122,4 @@ public class DefaultTechniqueDefLogic implements TechniqueDefLogic {
ambientLightColor.a = 1.0f;
return ambientLightColor;
}
@Override
public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights, int lastTexUnit) {
Renderer renderer = renderManager.getRenderer();
renderer.setShader(shader);
renderMeshFromGeometry(renderer, geometry);
}
}

@ -31,7 +31,6 @@
*/
package com.jme3.material.logic;
import com.jme3.asset.AssetManager;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.light.Light;
@ -41,20 +40,16 @@ import com.jme3.light.SpotLight;
import com.jme3.material.RenderState;
import com.jme3.material.TechniqueDef;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.math.Vector4f;
import com.jme3.renderer.Caps;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.Renderer;
import com.jme3.scene.Geometry;
import com.jme3.shader.DefineList;
import com.jme3.shader.Shader;
import com.jme3.shader.Uniform;
import com.jme3.shader.VarType;
import com.jme3.util.TempVars;
import java.util.EnumSet;
public final class MultiPassLightingLogic extends DefaultTechniqueDefLogic {
@ -73,7 +68,7 @@ public final class MultiPassLightingLogic extends DefaultTechniqueDefLogic {
}
@Override
public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights, int lastTexUnit) {
public void render(RenderManager renderManager, Shader shader, Geometry geometry, int lastTexUnit) {
Renderer r = renderManager.getRenderer();
Uniform lightDir = shader.getUniform("g_LightDirection");
Uniform lightColor = shader.getUniform("g_LightColor");
@ -82,6 +77,7 @@ public final class MultiPassLightingLogic extends DefaultTechniqueDefLogic {
boolean isFirstLight = true;
boolean isSecondLight = false;
LightList lights = getFilteredLightList(renderManager, geometry);
getAmbientColor(lights, false, ambientLightColor);
for (int i = 0; i < lights.size(); i++) {

@ -34,13 +34,19 @@ package com.jme3.material.logic;
import com.jme3.asset.AssetManager;
import com.jme3.bounding.BoundingSphere;
import com.jme3.light.*;
import static com.jme3.light.Light.Type.Directional;
import static com.jme3.light.Light.Type.Spot;
import com.jme3.material.*;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.math.*;
import com.jme3.renderer.*;
import com.jme3.scene.Geometry;
import com.jme3.shader.*;
import com.jme3.util.TempVars;
import com.jme3.shadow.next.array.ArrayShadowMap;
import com.jme3.shadow.next.array.ArrayShadowMapSlice;
import com.jme3.shadow.next.array.DirectionalArrayShadowMap;
import com.jme3.texture.TextureArray;
import java.util.Comparator;
import java.util.*;
@ -48,10 +54,16 @@ public final class SinglePassAndImageBasedLightingLogic extends DefaultTechnique
private static final String DEFINE_SINGLE_PASS_LIGHTING = "SINGLE_PASS_LIGHTING";
private static final String DEFINE_NB_LIGHTS = "NB_LIGHTS";
private static final String DEFINE_NB_PROBES = "NB_PROBES";
private static final String DEFINE_INDIRECT_LIGHTING = "INDIRECT_LIGHTING";
private static final String DEFINE_IN_PASS_SHADOWS = "IN_PASS_SHADOWS";
private static final String DEFINE_NUM_PSSM_SPLITS = "NUM_PSSM_SPLITS";
private static final RenderState ADDITIVE_LIGHT = new RenderState();
private final ColorRGBA ambientLightColor = new ColorRGBA(0, 0, 0, 1);
private TextureArray shadowMapArray;
private Vector3f pssmSplitsPositions;
private int numPssmSplits;
private static final String DEFINE_NB_PROBES = "NB_PROBES";
private List<LightProbe> lightProbes = new ArrayList<>(3);
static {
@ -60,33 +72,79 @@ public final class SinglePassAndImageBasedLightingLogic extends DefaultTechnique
}
private final int singlePassLightingDefineId;
private final int inPassShadowsDefineId;
private final int nbLightsDefineId;
private final int numPssmSplitsDefineId;
private final int nbProbesDefineId;
public SinglePassAndImageBasedLightingLogic(TechniqueDef techniqueDef) {
super(techniqueDef);
numPssmSplitsDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_NUM_PSSM_SPLITS, VarType.Int);
singlePassLightingDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_SINGLE_PASS_LIGHTING, VarType.Boolean);
nbLightsDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_NB_LIGHTS, VarType.Int);
inPassShadowsDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_IN_PASS_SHADOWS, VarType.Boolean);
nbProbesDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_NB_PROBES, VarType.Int);
}
@Override
public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager,
EnumSet<Caps> rendererCaps, LightList lights, DefineList defines) {
defines.set(nbLightsDefineId, renderManager.getSinglePassLightBatchSize() * 3);
EnumSet<Caps> rendererCaps, Geometry geometry, DefineList defines) {
defines.set(singlePassLightingDefineId, true);
//TODO here we have a problem, this is called once before render, so the define will be set for all passes (in case we have more than NB_LIGHTS lights)
//Though the second pass should not render IBL as it is taken care of on first pass like ambient light in phong lighting.
//We cannot change the define between passes and the old technique, and for some reason the code fails on mac (renders nothing).
if(lights != null) {
lightProbes.clear();
extractIndirectLights(lights, false);
defines.set(nbProbesDefineId, lightProbes.size());
// TODO: here we have a problem, this is called once before render,
// so the define will be set for all passes (in case we have more than NB_LIGHTS lights)
// Though the second pass should not render IBL as it is taken care of on
// first pass like ambient light in phong lighting.
// We cannot change the define between passes and the old technique, and
// for some reason the code fails on mac (renders nothing).
getFilteredLightList(renderManager, geometry);
ambientLightColor.set(0, 0, 0, 1);
lightProbes.clear();
pssmSplitsPositions = null;
numPssmSplits = 0;
for (int i = 0; i < filteredLightList.size(); i++) {
Light light = filteredLightList.get(i);
if (light instanceof AmbientLight) {
ambientLightColor.addLocal(light.getColor());
filteredLightList.remove(i--);
} else if (light instanceof LightProbe) {
lightProbes.add((LightProbe) light);
filteredLightList.remove(i--);
} else if (light.getShadowMap() != null) {
ArrayShadowMap shadowMap = (ArrayShadowMap) light.getShadowMap();
shadowMapArray = shadowMap.getArray();
if (light.getType() == Light.Type.Directional) {
numPssmSplits = shadowMap.getNumSlices();
pssmSplitsPositions = ((DirectionalArrayShadowMap) shadowMap).getProjectionSplitPositions();
}
}
}
return super.makeCurrent(assetManager, renderManager, rendererCaps, lights, defines);
defines.set(nbProbesDefineId, lightProbes.size());
ambientLightColor.a = 1.0f;
filteredLightList.sort(new Comparator<Light>() {
@Override
public int compare(Light a, Light b) {
boolean shadA = a.getShadowMap() != null;
boolean shadB = b.getShadowMap() != null;
if (shadA != shadB) {
return shadA ? -1 : 1;
} else {
int ordA = a.getType().ordinal();
int ordB = b.getType().ordinal();
return ordB - ordA;
}
}
});
defines.set(nbLightsDefineId, renderManager.getSinglePassLightBatchSize() * 3);
defines.set(inPassShadowsDefineId, shadowMapArray != null);
defines.set(numPssmSplitsDefineId, numPssmSplits);
return super.makeCurrent(assetManager, renderManager, rendererCaps, geometry, defines);
}
/**
@ -123,13 +181,11 @@ public final class SinglePassAndImageBasedLightingLogic extends DefaultTechnique
Uniform shCoeffs3 = shader.getUniform("g_ShCoeffs3");
Uniform lightProbePemMap3 = shader.getUniform("g_PrefEnvMap3");
lightProbes.clear();
if (startIndex != 0) {
// apply additive blending for 2nd and future passes
rm.getRenderer().applyRenderState(ADDITIVE_LIGHT);
ambientColor.setValue(VarType.Vector4, ColorRGBA.Black);
}else{
extractIndirectLights(lightList,true);
} else {
ambientColor.setValue(VarType.Vector4, ambientLightColor);
}
@ -150,88 +206,103 @@ public final class SinglePassAndImageBasedLightingLogic extends DefaultTechnique
lightProbeData.setValue(VarType.Matrix4, LightProbe.FALLBACK_MATRIX);
}
Uniform shadowMatricesUniform = shader.getUniform("g_ShadowMatrices");
shadowMatricesUniform.setMatrix4Length(numLights + numPssmSplits);
int shadowMatrixIndex = numPssmSplits;
int lightDataIndex = 0;
TempVars vars = TempVars.get();
Vector4f tmpVec = vars.vect4f1;
int curIndex;
int endIndex = numLights + startIndex;
for (curIndex = startIndex; curIndex < endIndex && curIndex < lightList.size(); curIndex++) {
Light l = lightList.get(curIndex);
if(l.getType() == Light.Type.Ambient){
endIndex++;
continue;
int endIndex = Math.min(startIndex + numLights, lightList.size());
ArrayShadowMap directionalShadowMap = null;
for (curIndex = startIndex; curIndex < endIndex; curIndex++) {
Light light = lightList.get(curIndex);
if (light.getType() == Light.Type.Ambient || light.getType() == Light.Type.Probe) {
throw new AssertionError();
}
ColorRGBA color = l.getColor();
//Color
if(l.getType() != Light.Type.Probe){
lightData.setVector4InArray(color.getRed(),
color.getGreen(),
color.getBlue(),
l.getType().getId(),
lightDataIndex);
lightDataIndex++;
if (light.getShadowMap() != null) {
ArrayShadowMap shadowMap = (ArrayShadowMap) light.getShadowMap();
if (light.getType() == Directional) {
directionalShadowMap = shadowMap;
} else if (light.getType() == Spot) {
for (int j = 0; j < shadowMap.getNumSlices(); j++) {
ArrayShadowMapSlice slice = (ArrayShadowMapSlice) shadowMap.getSlice(j);
shadowMatricesUniform.setMatrix4InArray(
slice.getBiasedViewProjectionMatrix(),
shadowMatrixIndex);
shadowMatrixIndex++;
}
}
}
ColorRGBA color = light.getColor();
lightData.setVector4InArray(
color.getRed(),
color.getGreen(),
color.getBlue(),
encodeLightTypeAndShadowMapIndex(light),
lightDataIndex++);
switch (l.getType()) {
case Directional:
DirectionalLight dl = (DirectionalLight) l;
switch (light.getType()) {
case Directional: {
DirectionalLight dl = (DirectionalLight) light;
Vector3f dir = dl.getDirection();
//Data directly sent in view space to avoid a matrix mult for each pixel
tmpVec.set(dir.getX(), dir.getY(), dir.getZ(), 0.0f);
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), -1, lightDataIndex);
lightDataIndex++;
//PADDING
lightData.setVector4InArray(0,0,0,0, lightDataIndex);
lightDataIndex++;
lightData.setVector4InArray(dir.getX(), dir.getY(), dir.getZ(), -1, lightDataIndex++);
lightData.setVector4InArray(0, 0, 0, 0, lightDataIndex++);
break;
case Point:
PointLight pl = (PointLight) l;
}
case Point: {
PointLight pl = (PointLight) light;
Vector3f pos = pl.getPosition();
float invRadius = pl.getInvRadius();
tmpVec.set(pos.getX(), pos.getY(), pos.getZ(), 1.0f);
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), invRadius, lightDataIndex);
lightDataIndex++;
//PADDING
lightData.setVector4InArray(0,0,0,0, lightDataIndex);
lightDataIndex++;
lightData.setVector4InArray(pos.getX(), pos.getY(), pos.getZ(), invRadius, lightDataIndex++);
lightData.setVector4InArray(0, 0, 0, 0, lightDataIndex++);
break;
case Spot:
SpotLight sl = (SpotLight) l;
Vector3f pos2 = sl.getPosition();
Vector3f dir2 = sl.getDirection();
}
case Spot: {
SpotLight sl = (SpotLight) light;
Vector3f pos = sl.getPosition();
Vector3f dir = sl.getDirection();
float invRange = sl.getInvSpotRange();
float spotAngleCos = sl.getPackedAngleCos();
tmpVec.set(pos2.getX(), pos2.getY(), pos2.getZ(), 1.0f);
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), invRange, lightDataIndex);
lightDataIndex++;
tmpVec.set(dir2.getX(), dir2.getY(), dir2.getZ(), 0.0f);
lightData.setVector4InArray(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), spotAngleCos, lightDataIndex);
lightDataIndex++;
lightData.setVector4InArray(pos.getX(), pos.getY(), pos.getZ(), invRange, lightDataIndex++);
lightData.setVector4InArray(dir.getX(), dir.getY(), dir.getZ(), spotAngleCos, lightDataIndex++);
break;
}
default:
throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
throw new UnsupportedOperationException("Unknown type of light: " + light.getType());
}
}
vars.release();
//Padding of unsued buffer space
while(lightDataIndex < numLights * 3) {
lightData.setVector4InArray(0f, 0f, 0f, 0f, lightDataIndex);
lightDataIndex++;
// Padding of unsued buffer space
while (lightDataIndex < numLights * 3) {
lightData.setVector4InArray(0f, 0f, 0f, 0f, lightDataIndex++);
}
if (directionalShadowMap != null) {
for (int i = 0; i < numPssmSplits; i++) {
ArrayShadowMapSlice slice = (ArrayShadowMapSlice) directionalShadowMap.getSlice(i);
shadowMatricesUniform.setMatrix4InArray(slice.getBiasedViewProjectionMatrix(), i);
}
}
if (shadowMapArray != null) {
rm.getRenderer().setTexture(lastTexUnit, shadowMapArray);
shader.getUniform("g_ShadowMapArray").setValue(VarType.Int, lastTexUnit);
}
if (pssmSplitsPositions != null) {
shader.getUniform("g_PssmSplits").setValue(VarType.Vector3, pssmSplitsPositions);
}
return curIndex;
}
private int setProbeData(RenderManager rm, int lastTexUnit, Uniform lightProbeData, Uniform shCoeffs, Uniform lightProbePemMap, LightProbe lightProbe) {
lightProbeData.setValue(VarType.Matrix4, lightProbe.getUniformMatrix());
//setVector4InArray(lightProbe.getPosition().x, lightProbe.getPosition().y, lightProbe.getPosition().z, 1f / area.getRadius() + lightProbe.getNbMipMaps(), 0);
shCoeffs.setValue(VarType.Vector3Array, lightProbe.getShCoeffs());
//assigning new texture indexes
int pemUnit = lastTexUnit++;
@ -241,43 +312,20 @@ public final class SinglePassAndImageBasedLightingLogic extends DefaultTechnique
}
@Override
public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights, int lastTexUnit) {
public void render(RenderManager renderManager, Shader shader, Geometry geometry, int lastTexUnit) {
int nbRenderedLights = 0;
Renderer renderer = renderManager.getRenderer();
int batchSize = renderManager.getSinglePassLightBatchSize();
if (lights.size() == 0) {
updateLightListUniforms(shader, geometry, lights,batchSize, renderManager, 0, lastTexUnit);
if (filteredLightList.size() == 0) {
updateLightListUniforms(shader, geometry, filteredLightList,batchSize, renderManager, 0, lastTexUnit);
renderer.setShader(shader);
renderMeshFromGeometry(renderer, geometry);
} else {
while (nbRenderedLights < lights.size()) {
nbRenderedLights = updateLightListUniforms(shader, geometry, lights, batchSize, renderManager, nbRenderedLights, lastTexUnit);
while (nbRenderedLights < filteredLightList.size()) {
nbRenderedLights = updateLightListUniforms(shader, geometry, filteredLightList, batchSize, renderManager, nbRenderedLights, lastTexUnit);
renderer.setShader(shader);
renderMeshFromGeometry(renderer, geometry);
}
}
return;
}
protected void extractIndirectLights(LightList lightList, boolean removeLights) {
ambientLightColor.set(0, 0, 0, 1);
for (int j = 0; j < lightList.size(); j++) {
Light l = lightList.get(j);
if (l instanceof AmbientLight) {
ambientLightColor.addLocal(l.getColor());
if(removeLights){
lightList.remove(l);
j--;
}
}
if (l instanceof LightProbe) {
lightProbes.add((LightProbe) l);
if(removeLights){
lightList.remove(l);
j--;
}
}
}
ambientLightColor.a = 1.0f;
}
}

@ -77,25 +77,31 @@ public final class SinglePassLightingLogic extends DefaultTechniqueDefLogic {
}
@Override
public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager,
EnumSet<Caps> rendererCaps, LightList lights, DefineList defines) {
public Shader makeCurrent(
AssetManager assetManager,
RenderManager renderManager,
EnumSet<Caps> rendererCaps,
Geometry geometry,
DefineList defines) {
defines.set(nbLightsDefineId, renderManager.getSinglePassLightBatchSize() * 3);
defines.set(singlePassLightingDefineId, true);
return super.makeCurrent(assetManager, renderManager, rendererCaps, lights, defines);
return super.makeCurrent(assetManager, renderManager, rendererCaps, geometry, defines);
}
/**
* Uploads the lights in the light list as two uniform arrays.<br/><br/> *
* Uploads the lights in the light list as two uniform arrays.<br><br>
*
* <p>
* <code>uniform vec4 g_LightColor[numLights];</code><br/> //
* g_LightColor.rgb is the diffuse/specular color of the light.<br/> //
* g_Lightcolor.a is the type of light, 0 = Directional, 1 = Point, <br/> //
* 2 = Spot. <br/> <br/>
* <code>uniform vec4 g_LightPosition[numLights];</code><br/> //
* g_LightPosition.xyz is the position of the light (for point lights)<br/>
* // or the direction of the light (for directional lights).<br/> //
* <code>uniform vec4 g_LightColor[numLights];</code><br>
* g_LightColor.rgb is the diffuse/specular color of the light.<br>
* g_Lightcolor.a is the type of light, 0 = Directional, 1 = Point, <br>
* 2 = Spot. <br> <br>
* <code>uniform vec4 g_LightPosition[numLights];</code><br>
* g_LightPosition.xyz is the position of the light (for point lights)<br>
* // or the direction of the light (for directional lights).<br>
* g_LightPosition.w is the inverse radius (1/r) of the light (for
* attenuation) <br/> </p>
* attenuation) <br>
* </p>
*/
protected int updateLightListUniforms(Shader shader, Geometry g, LightList lightList, int numLights, RenderManager rm, int startIndex) {
if (numLights == 0) { // this shader does not do lighting, ignore.
@ -131,7 +137,7 @@ public final class SinglePassLightingLogic extends DefaultTechniqueDefLogic {
lightData.setVector4InArray(color.getRed(),
color.getGreen(),
color.getBlue(),
l.getType().getId(),
encodeLightType(l),
lightDataIndex);
lightDataIndex++;
@ -198,10 +204,11 @@ public final class SinglePassLightingLogic extends DefaultTechniqueDefLogic {
}
@Override
public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights, int lastTexUnit) {
public void render(RenderManager renderManager, Shader shader, Geometry geometry, int lastTexUnit) {
int nbRenderedLights = 0;
Renderer renderer = renderManager.getRenderer();
int batchSize = renderManager.getSinglePassLightBatchSize();
LightList lights = getFilteredLightList(renderManager, geometry);
if (lights.size() == 0) {
updateLightListUniforms(shader, geometry, lights, batchSize, renderManager, 0);
renderer.setShader(shader);

@ -1,182 +0,0 @@
/*
* 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 com.jme3.material.logic;
import com.jme3.asset.AssetManager;
import com.jme3.light.DirectionalLight;
import com.jme3.light.Light;
import com.jme3.light.LightList;
import com.jme3.light.PointLight;
import com.jme3.light.SpotLight;
import com.jme3.material.TechniqueDef;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Matrix4f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Caps;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.Renderer;
import com.jme3.scene.Geometry;
import com.jme3.shader.DefineList;
import com.jme3.shader.Shader;
import com.jme3.shader.Uniform;
import com.jme3.shader.VarType;
import java.util.ArrayList;
import java.util.EnumSet;
/**
* Rendering logic for static pass.
*
* @author Kirill Vainer
*/
public final class StaticPassLightingLogic extends DefaultTechniqueDefLogic {
private static final String DEFINE_NUM_DIR_LIGHTS = "NUM_DIR_LIGHTS";
private static final String DEFINE_NUM_POINT_LIGHTS = "NUM_POINT_LIGHTS";
private static final String DEFINE_NUM_SPOT_LIGHTS = "NUM_SPOT_LIGHTS";
private final int numDirLightsDefineId;
private final int numPointLightsDefineId;
private final int numSpotLightsDefineId;
private final ArrayList<DirectionalLight> tempDirLights = new ArrayList<DirectionalLight>();
private final ArrayList<PointLight> tempPointLights = new ArrayList<PointLight>();
private final ArrayList<SpotLight> tempSpotLights = new ArrayList<SpotLight>();
private final ColorRGBA ambientLightColor = new ColorRGBA(0, 0, 0, 1);
private final Vector3f tempPosition = new Vector3f();
private final Vector3f tempDirection = new Vector3f();
public StaticPassLightingLogic(TechniqueDef techniqueDef) {
super(techniqueDef);
numDirLightsDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_NUM_DIR_LIGHTS, VarType.Int);
numPointLightsDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_NUM_POINT_LIGHTS, VarType.Int);
numSpotLightsDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_NUM_SPOT_LIGHTS, VarType.Int);
}
@Override
public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager,
EnumSet<Caps> rendererCaps, LightList lights, DefineList defines) {
// TODO: if it ever changes that render isn't called
// right away with the same geometry after makeCurrent, it would be
// a problem.
// Do a radix sort.
tempDirLights.clear();
tempPointLights.clear();
tempSpotLights.clear();
for (Light light : lights) {
switch (light.getType()) {
case Directional:
tempDirLights.add((DirectionalLight) light);
break;
case Point:
tempPointLights.add((PointLight) light);
break;
case Spot:
tempSpotLights.add((SpotLight) light);
break;
}
}
defines.set(numDirLightsDefineId, tempDirLights.size());
defines.set(numPointLightsDefineId, tempPointLights.size());
defines.set(numSpotLightsDefineId, tempSpotLights.size());
return techniqueDef.getShader(assetManager, rendererCaps, defines);
}
private void transformDirection(Matrix4f viewMatrix, Vector3f direction) {
viewMatrix.multNormal(direction, direction);
}
private void transformPosition(Matrix4f viewMatrix, Vector3f location) {
viewMatrix.mult(location, location);
}
private void updateLightListUniforms(Matrix4f viewMatrix, Shader shader, LightList lights) {
Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
ambientColor.setValue(VarType.Vector4, getAmbientColor(lights, true, ambientLightColor));
Uniform lightData = shader.getUniform("g_LightData");
int totalSize = tempDirLights.size() * 2
+ tempPointLights.size() * 2
+ tempSpotLights.size() * 3;
lightData.setVector4Length(totalSize);
int index = 0;
for (DirectionalLight light : tempDirLights) {
ColorRGBA color = light.getColor();
tempDirection.set(light.getDirection());
transformDirection(viewMatrix, tempDirection);
lightData.setVector4InArray(color.r, color.g, color.b, 1f, index++);
lightData.setVector4InArray(tempDirection.x, tempDirection.y, tempDirection.z, 1f, index++);
}
for (PointLight light : tempPointLights) {
ColorRGBA color = light.getColor();
tempPosition.set(light.getPosition());
float invRadius = light.getInvRadius();
transformPosition(viewMatrix, tempPosition);
lightData.setVector4InArray(color.r, color.g, color.b, 1f, index++);
lightData.setVector4InArray(tempPosition.x, tempPosition.y, tempPosition.z, invRadius, index++);
}
for (SpotLight light : tempSpotLights) {
ColorRGBA color = light.getColor();
Vector3f pos = light.getPosition();
Vector3f dir = light.getDirection();
tempPosition.set(light.getPosition());
tempDirection.set(light.getDirection());
transformPosition(viewMatrix, tempPosition);
transformDirection(viewMatrix, tempDirection);
float invRange = light.getInvSpotRange();
float spotAngleCos = light.getPackedAngleCos();
lightData.setVector4InArray(color.r, color.g, color.b, 1f, index++);
lightData.setVector4InArray(tempPosition.x, tempPosition.y, tempPosition.z, invRange, index++);
lightData.setVector4InArray(tempDirection.x, tempDirection.y, tempDirection.z, spotAngleCos, index++);
}
}
@Override
public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights, int lastTexUnit) {
Renderer renderer = renderManager.getRenderer();
Matrix4f viewMatrix = renderManager.getCurrentCamera().getViewMatrix();
updateLightListUniforms(viewMatrix, shader, lights);
renderer.setShader(shader);
renderMeshFromGeometry(renderer, geometry);
}
}

@ -32,7 +32,9 @@
package com.jme3.material.logic;
import com.jme3.asset.AssetManager;
import com.jme3.light.LightList;
import com.jme3.material.MatParam;
import com.jme3.material.RenderState;
import com.jme3.material.TechniqueDef;
import com.jme3.material.TechniqueDef.LightMode;
import com.jme3.renderer.Caps;
import com.jme3.renderer.RenderManager;
@ -63,19 +65,18 @@ public interface TechniqueDefLogic {
*
* @param assetManager The asset manager to use for loading shader source code,
* shader nodes, and and lookup textures.
* @param geometry The geometry being rendered
* @param renderManager The render manager for which rendering is to be performed.
* @param rendererCaps Renderer capabilities. The returned shader must
* support these capabilities.
* @param lights The lights with which the geometry shall be rendered. This
* list must not include culled lights.
* @param defines The define list used by the technique, any
* {@link TechniqueDef#addShaderUnmappedDefine(java.lang.String) unmapped defines}
* should be set here to change shader behavior.
*
* @return The shader to use for rendering.
*/
public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager,
EnumSet<Caps> rendererCaps, LightList lights, DefineList defines);
public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager,
EnumSet<Caps> rendererCaps, Geometry geometry, DefineList defines);
/**
* Requests that the <code>TechniqueDefLogic</code> renders the given geometry.
@ -88,10 +89,10 @@ public interface TechniqueDefLogic {
* can still be overriden.
*
* @param renderManager The render manager to perform the rendering against.
* * @param shader The shader that was selected by this logic in
* @param shader The shader that was selected by this logic in
* {@link #makeCurrent(com.jme3.asset.AssetManager, com.jme3.renderer.RenderManager, java.util.EnumSet, com.jme3.shader.DefineList)}.
* @param geometry The geometry to render
* @param lights Lights which influence the geometry.
* @param lastTextureUnit The last unused texture unit
*/
public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights, int lastTexUnit);
public void render(RenderManager renderManager, Shader shader, Geometry geometry, int lastTextureUnit);
}

@ -753,7 +753,6 @@ public final class Matrix4f implements Savable, Cloneable, java.io.Serializable
TempVars vars = TempVars.get();
fillFloatArray(vars.matrixWrite, columnMajor);
fb.put(vars.matrixWrite, 0, 16);

@ -60,7 +60,7 @@ public class PreDepthProcessor implements SceneProcessor {
preDepth.getAdditionalRenderState().setPolyOffset(0, 0);
preDepth.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Back);
forcedRS = new RenderState();
forcedRS = RenderState.ADDITIONAL.clone();
forcedRS.setDepthTest(true);
forcedRS.setDepthWrite(false);
}

@ -84,7 +84,6 @@ public class RenderManager {
private final SafeArrayList<MatParamOverride> forcedOverrides = new SafeArrayList<>(MatParamOverride.class);
private int viewX, viewY, viewWidth, viewHeight;
private final Matrix4f orthoMatrix = new Matrix4f();
private final LightList filteredLightList = new LightList(null);
private boolean handleTranlucentBucket = true;
private AppProfiler prof;
private LightFilter lightFilter = new DefaultLightFilter();
@ -562,15 +561,6 @@ public class RenderManager {
} else {
setWorldMatrix(geom.getWorldMatrix());
}
// Perform light filtering if we have a light filter.
LightList lightList = geom.getWorldLightList();
if (lightFilter != null) {
filteredLightList.clear();
lightFilter.filterLights(geom, filteredLightList);
lightList = filteredLightList;
}
Material material = geom.getMaterial();
@ -588,30 +578,22 @@ public class RenderManager {
: TechniqueDef.DEFAULT_TECHNIQUE_NAME;
geom.getMaterial().selectTechnique(forcedTechnique, this);
//saving forcedRenderState for future calls
RenderState tmpRs = forcedRenderState;
if (geom.getMaterial().getActiveTechnique().getDef().getForcedRenderState() != null) {
//forcing forced technique renderState
forcedRenderState = geom.getMaterial().getActiveTechnique().getDef().getForcedRenderState();
}
// use geometry's material
material.render(geom, lightList, this);
material.render(geom, this);
material.selectTechnique(previousTechniqueName, this);
//restoring forcedRenderState
forcedRenderState = tmpRs;
//Reverted this part from revision 6197
//If forcedTechnique does not exists, and forcedMaterial is not set, the geom MUST NOT be rendered
} else if (forcedMaterial != null) {
// use forced material
forcedMaterial.render(geom, lightList, this);
forcedMaterial.render(geom, this);
}
} else if (forcedMaterial != null) {
// use forced material
forcedMaterial.render(geom, lightList, this);
forcedMaterial.render(geom, this);
} else {
material.render(geom, lightList, this);
material.render(geom, this);
}
}

@ -221,7 +221,7 @@ public class GLDebugES extends GLDebug implements GL, GLFbo, GLExt {
@Override
public void glGenQueries(int num, IntBuffer ids) {
glGenQueries(num, ids);
gl.glGenQueries(num, ids);
checkError();
}

@ -90,6 +90,7 @@ public final class GLTracer implements InvocationHandler {
noEnumArgs("glTexImage3D", 1, 3, 4, 5, 6);
noEnumArgs("glTexSubImage2D", 1, 2, 3, 4, 5);
noEnumArgs("glTexSubImage3D", 1, 2, 3, 4, 5, 6, 7);
noEnumArgs("glTexImage2DMultisample", 1, 3, 4);
noEnumArgs("glCompressedTexImage2D", 1, 3, 4, 5);
noEnumArgs("glCompressedTexSubImage3D", 1, 2, 3, 4, 5, 6, 7);
noEnumArgs("glDeleteTextures", 0);
@ -101,6 +102,7 @@ public final class GLTracer implements InvocationHandler {
noEnumArgs("glVertexAttribPointer", 0, 1, 4, 5);
noEnumArgs("glVertexAttribDivisorARB", 0, 1);
noEnumArgs("glDrawRangeElements", 1, 2, 3, 5);
noEnumArgs("glDrawElementsInstancedARB", 1, 3, 4);
noEnumArgs("glDrawArrays", 1, 2);
noEnumArgs("glDeleteBuffers", 0);
noEnumArgs("glBindVertexArray", 0);

@ -199,10 +199,12 @@ final class TextureUtil {
format.format,
format.dataType,
data);
} else {
} else if (data != null) {
// For texture arrays, only upload 1 slice at a time.
// zoffset specifies slice index, and depth is 1 to indicate
// a single texture in the array.
// We don't need to do this for NULL data because the
// main texture storage was already allocated with slice == -1
gl2.glTexSubImage3D(target,
level, // level
0, // xoffset

@ -197,7 +197,7 @@ public class Uniform extends ShaderVariable {
}
}
public void setValue(VarType type, Object value){
public void setValue(VarType type, Object value) {
if (location == LOC_NOT_DEFINED) {
return;
}
@ -401,12 +401,40 @@ public class Uniform extends ShaderVariable {
varType = type;
updateNeeded = true;
}
public void setMatrix4Length(int length) {
if (location == LOC_NOT_DEFINED) {
return;
}
multiData = BufferUtils.ensureLargeEnough(multiData, length * 4 * 4);
value = multiData;
varType = VarType.Matrix4Array;
updateNeeded = true;
setByCurrentMaterial = true;
}
public void setMatrix4InArray(Matrix4f matrix, int index) {
if (location == LOC_NOT_DEFINED) {
return;
}
public void setVector4Length(int length){
if (location == -1) {
if (varType != null && varType != VarType.Matrix4Array) {
throw new IllegalArgumentException("Expected a " + varType.name() + " value!");
}
multiData.position(index * 4 * 4);
matrix.fillFloatBuffer(multiData, true);
multiData.rewind();
updateNeeded = true;
setByCurrentMaterial = true;
}
public void setVector4Length(int length) {
if (location == LOC_NOT_DEFINED) {
return;
}
multiData = BufferUtils.ensureLargeEnough(multiData, length * 4);
value = multiData;
varType = VarType.Vector4Array;
@ -414,8 +442,8 @@ public class Uniform extends ShaderVariable {
setByCurrentMaterial = true;
}
public void setVector4InArray(float x, float y, float z, float w, int index){
if (location == -1) {
public void setVector4InArray(float x, float y, float z, float w, int index) {
if (location == LOC_NOT_DEFINED) {
return;
}
@ -429,7 +457,7 @@ public class Uniform extends ShaderVariable {
updateNeeded = true;
setByCurrentMaterial = true;
}
public boolean isUpdateNeeded(){
return updateNeeded;
}

@ -94,7 +94,7 @@ public abstract class AbstractShadowRenderer implements SceneProcessor, Savable,
protected EdgeFilteringMode edgeFilteringMode = EdgeFilteringMode.Bilinear;
protected CompareMode shadowCompareMode = CompareMode.Hardware;
protected Picture[] dispPic;
protected RenderState forcedRenderState = new RenderState();
protected RenderState forcedRenderState = RenderState.ADDITIONAL.clone();
protected boolean renderBackFacesShadows = true;
protected AppProfiler prof;

@ -158,7 +158,6 @@ public class BasicShadowRenderer implements SceneProcessor {
ShadowUtil.updateFrustumPoints(viewCam,
viewCam.getFrustumNear(),
viewCam.getFrustumFar(),
1.0f,
points);
Vector3f frustaCenter = new Vector3f();

@ -148,7 +148,7 @@ public class DirectionalLightShadowRenderer extends AbstractShadowRenderer {
//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);
ShadowUtil.updateFrustumPoints(viewCam, frustumNear, zFar, points);
shadowCam.setFrustumFar(zFar);
shadowCam.getRotation().lookAt(light.getDirection(), shadowCam.getUp());
@ -183,7 +183,7 @@ public class DirectionalLightShadowRenderer extends AbstractShadowRenderer {
protected GeometryList getOccludersToRender(int shadowMapIndex, GeometryList shadowMapOccluders) {
// update frustum points based on current camera and split
ShadowUtil.updateFrustumPoints(viewPort.getCamera(), splitsArray[shadowMapIndex], splitsArray[shadowMapIndex + 1], 1.0f, points);
ShadowUtil.updateFrustumPoints(viewPort.getCamera(), splitsArray[shadowMapIndex], splitsArray[shadowMapIndex + 1], points);
//Updating shadow cam with current split frusta
if (lightReceivers.size()==0) {

@ -398,7 +398,7 @@ public class PssmShadowRenderer implements SceneProcessor {
//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);
ShadowUtil.updateFrustumPoints(viewCam, frustumNear, zFar, points);
//shadowCam.setDirection(direction);
shadowCam.getRotation().lookAt(direction, shadowCam.getUp());
@ -428,7 +428,7 @@ public class PssmShadowRenderer implements SceneProcessor {
for (int i = 0; i < nbSplits; i++) {
// update frustum points based on current camera and split
ShadowUtil.updateFrustumPoints(viewCam, splitsArray[i], splitsArray[i + 1], 1.0f, points);
ShadowUtil.updateFrustumPoints(viewCam, splitsArray[i], splitsArray[i + 1], points);
//Updating shadow cam with current split frusta
ShadowUtil.updateShadowCamera(viewPort, lightReceivers, shadowCam, points, splitOccluders, shadowMapSize);

@ -67,15 +67,4 @@ public final class PssmShadowUtil {
splits[0] = near;
splits[splits.length - 1] = far;
}
/**
* Compute the Zfar in the model vieuw to adjust the Zfar distance for the splits calculation
*/
public static float computeZFar(GeometryList occ, GeometryList recv, Camera cam) {
Matrix4f mat = cam.getViewMatrix();
BoundingBox bbOcc = ShadowUtil.computeUnionBound(occ, mat);
BoundingBox bbRecv = ShadowUtil.computeUnionBound(recv, mat);
return min(max(bbOcc.getZExtent() - bbOcc.getCenter().z, bbRecv.getZExtent() - bbRecv.getCenter().z), cam.getFrustumFar());
}
}

@ -32,7 +32,9 @@
package com.jme3.shadow;
import com.jme3.bounding.BoundingBox;
import com.jme3.bounding.BoundingSphere;
import com.jme3.bounding.BoundingVolume;
import com.jme3.collision.UnsupportedCollisionException;
import com.jme3.math.FastMath;
import com.jme3.math.Matrix4f;
import com.jme3.math.Transform;
@ -42,6 +44,7 @@ import com.jme3.renderer.Camera;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.GeometryList;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
@ -96,7 +99,6 @@ public class ShadowUtil {
public static void updateFrustumPoints(Camera viewCam,
float nearOverride,
float farOverride,
float scale,
Vector3f[] points) {
Vector3f pos = viewCam.getLocation();
@ -149,22 +151,6 @@ public class ShadowUtil {
points[5].set(farCenter).addLocal(farUp).subtractLocal(farRight);
points[6].set(farCenter).addLocal(farUp).addLocal(farRight);
points[7].set(farCenter).subtractLocal(farUp).addLocal(farRight);
if (scale != 1.0f) {
// find center of frustum
Vector3f center = new Vector3f();
for (int i = 0; i < 8; i++) {
center.addLocal(points[i]);
}
center.divideLocal(8f);
Vector3f cDir = new Vector3f();
for (int i = 0; i < 8; i++) {
cDir.set(points[i]).subtractLocal(center);
cDir.multLocal(scale - 1.0f);
points[i].addLocal(cDir);
}
}
}
/**
@ -270,65 +256,8 @@ public class ShadowUtil {
max.maxLocal(temp);
}
vars.release();
Vector3f center = min.add(max).multLocal(0.5f);
Vector3f extent = max.subtract(min).multLocal(0.5f);
//Nehon 08/18/2010 : Added an offset to the extend to avoid banding artifacts when the frustum are aligned
return new BoundingBox(center, extent.x + 2.0f, extent.y + 2.0f, extent.z + 2.5f);
}
/**
* Updates the shadow camera to properly contain the given points (which
* contain the eye camera frustum corners)
*
* @param shadowCam
* @param points
*/
public static void updateShadowCamera(Camera shadowCam, Vector3f[] points) {
boolean ortho = shadowCam.isParallelProjection();
shadowCam.setProjectionMatrix(null);
if (ortho) {
shadowCam.setFrustum(-1, 1, -1, 1, 1, -1);
} else {
shadowCam.setFrustumPerspective(45, 1, 1, 150);
}
Matrix4f viewProjMatrix = shadowCam.getViewProjectionMatrix();
Matrix4f projMatrix = shadowCam.getProjectionMatrix();
BoundingBox splitBB = computeBoundForPoints(points, viewProjMatrix);
TempVars vars = TempVars.get();
Vector3f splitMin = splitBB.getMin(vars.vect1);
Vector3f splitMax = splitBB.getMax(vars.vect2);
// splitMin.z = 0;
// Create the crop matrix.
float scaleX, scaleY, scaleZ;
float offsetX, offsetY, offsetZ;
scaleX = 2.0f / (splitMax.x - splitMin.x);
scaleY = 2.0f / (splitMax.y - splitMin.y);
offsetX = -0.5f * (splitMax.x + splitMin.x) * scaleX;
offsetY = -0.5f * (splitMax.y + splitMin.y) * scaleY;
scaleZ = 1.0f / (splitMax.z - splitMin.z);
offsetZ = -splitMin.z * scaleZ;
Matrix4f cropMatrix = vars.tempMat4;
cropMatrix.set(scaleX, 0f, 0f, offsetX,
0f, scaleY, 0f, offsetY,
0f, 0f, scaleZ, offsetZ,
0f, 0f, 0f, 1f);
Matrix4f result = new Matrix4f();
result.set(cropMatrix);
result.multLocal(projMatrix);
vars.release();
shadowCam.setProjectionMatrix(result);
return new BoundingBox(min, max);
}
/**
@ -342,7 +271,6 @@ public class ShadowUtil {
{
// global variables set in order not to have recursive process method with too many parameters
Matrix4f viewProjMatrix;
public Integer casterCount;
BoundingBox splitBB, casterBB;
GeometryList splitOccluders;
TempVars vars;
@ -350,9 +278,8 @@ public class ShadowUtil {
public OccludersExtractor() {}
// initialize the global OccludersExtractor variables
public OccludersExtractor(Matrix4f vpm, int cc, BoundingBox sBB, BoundingBox cBB, GeometryList sOCC, TempVars v) {
viewProjMatrix = vpm;
casterCount = cc;
public OccludersExtractor(Matrix4f vpm, BoundingBox sBB, BoundingBox cBB, GeometryList sOCC, TempVars v) {
viewProjMatrix = vpm;
splitBB = sBB;
casterBB = cBB;
splitOccluders = sOCC;
@ -364,82 +291,78 @@ public class ShadowUtil {
* The global OccludersExtractor variables need to be initialized first.
* Variables are updated and used in {@link ShadowUtil#updateShadowCamera} at last.
*/
public int addOccluders(Spatial scene) {
public void addOccluders(Spatial scene) {
if ( scene != null ) process(scene);
return casterCount;
}
private boolean intersectsIgnoreNearZ(BoundingBox splitBB, BoundingSphere occSphere) {
float distSqr = occSphere.getRadius() * occSphere.getRadius();
float minX = splitBB.getCenter().x - splitBB.getXExtent();
float maxX = splitBB.getCenter().x + splitBB.getXExtent();
float minY = splitBB.getCenter().y - splitBB.getYExtent();
float maxY = splitBB.getCenter().y + splitBB.getYExtent();
float maxZ = splitBB.getCenter().z + splitBB.getZExtent();
if (occSphere.getCenter().x < minX) distSqr -= FastMath.sqr(occSphere.getCenter().x - minX);
else if (occSphere.getCenter().x > maxX) distSqr -= FastMath.sqr(occSphere.getCenter().x - maxX);
if (occSphere.getCenter().y < minY) distSqr -= FastMath.sqr(occSphere.getCenter().y - minY);
else if (occSphere.getCenter().y > maxY) distSqr -= FastMath.sqr(occSphere.getCenter().y - maxY);
if (occSphere.getCenter().z > maxZ) distSqr -= FastMath.sqr(occSphere.getCenter().z - maxZ);
return distSqr > 0;
}
private boolean intersectsIgnoreNearZ(BoundingBox splitBB, BoundingBox occBB) {
if (splitBB.getCenter().x + splitBB.getXExtent() < occBB.getCenter().x - occBB.getXExtent()
|| splitBB.getCenter().x - splitBB.getXExtent() > occBB.getCenter().x + occBB.getXExtent()) {
return false;
} else if (splitBB.getCenter().y + splitBB.getYExtent() < occBB.getCenter().y - occBB.getYExtent()
|| splitBB.getCenter().y - splitBB.getYExtent() > occBB.getCenter().y + occBB.getYExtent()) {
return false;
} else if (splitBB.getCenter().z + splitBB.getZExtent() < occBB.getCenter().z - occBB.getZExtent()) {
return false;
} else {
return true;
}
}
private boolean intersectsIgnoreNearZ(BoundingBox splitBB, BoundingVolume occBV) {
if (occBV instanceof BoundingBox) {
return intersectsIgnoreNearZ(splitBB, (BoundingBox) occBV);
} else if (occBV instanceof BoundingSphere) {
return intersectsIgnoreNearZ(splitBB, (BoundingSphere) occBV);
} else {
throw new UnsupportedCollisionException("With: " + occBV.getClass().getSimpleName());
}
}
private void process(Spatial scene) {
if (scene.getCullHint() == Spatial.CullHint.Always) return;
RenderQueue.ShadowMode shadowMode = scene.getShadowMode();
if ( scene instanceof Geometry )
{
if (scene instanceof Geometry) {
// convert bounding box to light's viewproj space
Geometry occluder = (Geometry)scene;
if (shadowMode != RenderQueue.ShadowMode.Off && shadowMode != RenderQueue.ShadowMode.Receive
&& !occluder.isGrouped() && occluder.getWorldBound()!=null) {
Geometry occluder = (Geometry) scene;
ShadowMode shadowMode = scene.getShadowMode();
if (shadowMode != ShadowMode.Off && shadowMode != ShadowMode.Receive
&& !occluder.isGrouped()) {
BoundingVolume bv = occluder.getWorldBound();
BoundingVolume occBox = bv.transform(viewProjMatrix, vars.bbox);
boolean intersects = splitBB.intersects(occBox);
if (!intersects && occBox instanceof BoundingBox) {
BoundingBox occBB = (BoundingBox) occBox;
//Kirill 01/10/2011
// Extend the occluder further into the frustum
// This fixes shadow disappearing issues when
// the caster itself is not in the view camera
// but its shadow is in the camera
// The number is in world units
occBB.setZExtent(occBB.getZExtent() + 50);
occBB.setCenter(occBB.getCenter().addLocal(0, 0, 25));
if (splitBB.intersects(occBB)) {
//Nehon : prevent NaN and infinity values to screw the final bounding box
if (!Float.isNaN(occBox.getCenter().x) && !Float.isInfinite(occBox.getCenter().x)) {
// To prevent extending the depth range too much
// We return the bound to its former shape
// Before adding it
occBB.setZExtent(occBB.getZExtent() - 50);
occBB.setCenter(occBB.getCenter().subtractLocal(0, 0, 25));
casterBB.mergeLocal(occBox);
casterCount++;
}
if (splitOccluders != null) {
splitOccluders.add(occluder);
}
}
} else if (intersects) {
if (intersectsIgnoreNearZ(splitBB, occBox)) {
casterBB.mergeLocal(occBox);
casterCount++;
if (splitOccluders != null) {
splitOccluders.add(occluder);
}
}
}
}
else if ( scene instanceof Node && ((Node)scene).getWorldBound()!=null )
{
Node nodeOcc = (Node)scene;
boolean intersects = false;
// some
BoundingVolume bv = nodeOcc.getWorldBound();
} else if (scene instanceof Node) {
BoundingVolume bv = scene.getWorldBound();
BoundingVolume occBox = bv.transform(viewProjMatrix, vars.bbox);
intersects = splitBB.intersects(occBox);
if (!intersects && occBox instanceof BoundingBox) {
BoundingBox occBB = (BoundingBox) occBox;
//Kirill 01/10/2011
// Extend the occluder further into the frustum
// This fixes shadow disappearing issues when
// the caster itself is not in the view camera
// but its shadow is in the camera
// The number is in world units
occBB.setZExtent(occBB.getZExtent() + 50);
occBB.setCenter(occBB.getCenter().addLocal(0, 0, 25));
intersects = splitBB.intersects(occBB);
}
if ( intersects ) {
if (intersectsIgnoreNearZ(splitBB, occBox)) {
for (Spatial child : ((Node)scene).getChildren()) {
process(child);
}
@ -457,7 +380,7 @@ public class ShadowUtil {
GeometryList receivers,
Camera shadowCam,
Vector3f[] points,
GeometryList splitOccluders,
GeometryList shadowCasters,
float shadowMapSize) {
boolean ortho = shadowCam.isParallelProjection();
@ -465,7 +388,7 @@ public class ShadowUtil {
shadowCam.setProjectionMatrix(null);
if (ortho) {
shadowCam.setFrustum(-shadowCam.getFrustumFar(), shadowCam.getFrustumFar(), -1, 1, 1, -1);
shadowCam.setFrustum(-1, 1, -1, 1, 1, -1);
}
// create transform to rotate points to viewspace
@ -478,36 +401,31 @@ public class ShadowUtil {
BoundingBox casterBB = new BoundingBox();
BoundingBox receiverBB = new BoundingBox();
int casterCount = 0, receiverCount = 0;
for (int i = 0; i < receivers.size(); i++) {
// convert bounding box to light's viewproj space
Geometry receiver = receivers.get(i);
BoundingVolume bv = receiver.getWorldBound();
BoundingVolume recvBox = bv.transform(viewProjMatrix, vars.bbox);
if (splitBB.intersects(recvBox)) {
//Nehon : prevent NaN and infinity values to screw the final bounding box
if (!Float.isNaN(recvBox.getCenter().x) && !Float.isInfinite(recvBox.getCenter().x)) {
receiverBB.mergeLocal(recvBox);
receiverCount++;
if (receivers != null && receivers.size() != 0) {
for (int i = 0; i < receivers.size(); i++) {
// convert bounding box to light's viewproj space
Geometry receiver = receivers.get(i);
BoundingVolume bv = receiver.getWorldBound();
BoundingVolume recvBox = bv.transform(viewProjMatrix, vars.bbox);
if (splitBB.intersects(recvBox)) {
//Nehon : prevent NaN and infinity values to screw the final bounding box
if (!Float.isNaN(recvBox.getCenter().x) && !Float.isInfinite(recvBox.getCenter().x)) {
receiverBB.mergeLocal(recvBox);
}
}
}
} else {
receiverBB.setXExtent(Float.POSITIVE_INFINITY);
receiverBB.setYExtent(Float.POSITIVE_INFINITY);
receiverBB.setZExtent(Float.POSITIVE_INFINITY);
}
// collect splitOccluders through scene recursive traverse
OccludersExtractor occExt = new OccludersExtractor(viewProjMatrix, casterCount, splitBB, casterBB, splitOccluders, vars);
OccludersExtractor occExt = new OccludersExtractor(viewProjMatrix, splitBB, casterBB, shadowCasters, vars);
for (Spatial scene : viewPort.getScenes()) {
occExt.addOccluders(scene);
}
casterCount = occExt.casterCount;
//Nehon 08/18/2010 this is to avoid shadow bleeding when the ground is set to only receive shadows
if (casterCount != receiverCount) {
casterBB.setXExtent(casterBB.getXExtent() + 2.0f);
casterBB.setYExtent(casterBB.getYExtent() + 2.0f);
casterBB.setZExtent(casterBB.getZExtent() + 2.0f);
}
Vector3f casterMin = casterBB.getMin(vars.vect1);
Vector3f casterMax = casterBB.getMax(vars.vect2);
@ -518,27 +436,26 @@ public class ShadowUtil {
Vector3f splitMin = splitBB.getMin(vars.vect5);
Vector3f splitMax = splitBB.getMax(vars.vect6);
splitMin.z = 0;
// if (!ortho) {
// shadowCam.setFrustumPerspective(45, 1, 1, splitMax.z);
// }
Matrix4f projMatrix = shadowCam.getProjectionMatrix();
Vector3f cropMin = vars.vect7;
Vector3f cropMax = vars.vect8;
// IMPORTANT: Special handling for Z values
cropMin.x = max(max(casterMin.x, receiverMin.x), splitMin.x);
cropMax.x = min(min(casterMax.x, receiverMax.x), splitMax.x);
cropMin.y = max(max(casterMin.y, receiverMin.y), splitMin.y);
cropMax.y = min(min(casterMax.y, receiverMax.y), splitMax.y);
cropMin.z = min(casterMin.z, splitMin.z);
cropMax.z = min(receiverMax.z, splitMax.z);
if (shadowCasters.size() > 0) {
// IMPORTANT: Special handling for Z values
cropMin.x = max(max(casterMin.x, receiverMin.x), splitMin.x);
cropMax.x = min(min(casterMax.x, receiverMax.x), splitMax.x);
cropMin.y = max(max(casterMin.y, receiverMin.y), splitMin.y);
cropMax.y = min(min(casterMax.y, receiverMax.y), splitMax.y);
cropMin.z = min(casterMin.z, splitMin.z);
cropMax.z = min(receiverMax.z, splitMax.z);
} else {
// Set crop = split so that everything in the scene has a depth < 1.0 in light space.
// This avoids shadowing everything when there are no casters.
cropMin.set(splitMin);
cropMax.set(splitMax);
}
// Create the crop matrix.
float scaleX, scaleY, scaleZ;

@ -137,7 +137,7 @@ public class SpotLightShadowRenderer extends AbstractShadowRenderer {
//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);
ShadowUtil.updateFrustumPoints(viewCam, frustumNear, zFar, points);
//shadowCam.setDirection(direction);
shadowCam.setFrustumPerspective(light.getSpotOuterAngle() * FastMath.RAD_TO_DEG * 2.0f, 1, 1f, light.getSpotRange());

@ -0,0 +1,275 @@
/*
* Copyright (c) 2009-2016 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.next;
import com.jme3.asset.AssetManager;
import com.jme3.shadow.next.pssm.DirectionalShadowParameters;
import com.jme3.light.DirectionalLight;
import com.jme3.light.Light;
import com.jme3.light.Light.Type;
import com.jme3.light.PointLight;
import com.jme3.light.SpotLight;
import com.jme3.material.MatParamOverride;
import com.jme3.material.RenderState;
import com.jme3.math.Vector3f;
import com.jme3.post.SceneProcessor;
import com.jme3.profile.AppProfiler;
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.scene.Node;
import com.jme3.shader.VarType;
import com.jme3.shadow.next.array.DirectionalArrayShadowMap;
import com.jme3.shadow.next.array.PointArrayShadowMap;
import com.jme3.shadow.next.array.SpotArrayShadowMap;
import com.jme3.texture.FrameBuffer;
import com.jme3.texture.Image;
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.TextureArray;
import com.jme3.texture.image.ColorSpace;
import com.jme3.util.ListMap;
import com.jme3.util.TempVars;
import java.nio.ByteBuffer;
import java.util.ArrayList;
/**
* The 4th generation of shadow mapping in jME3.
* <p>
* This version is primarily focused on rendering in-pass shadows, so pre-pass
* and subsequent stages are separated.
*
* @author Kirill Vainer
*/
public class InPassShadowRenderer implements SceneProcessor {
private static final String PRE_SHADOW_TECHNIQUE_NAME = "PreShadow";
private RenderManager renderManager;
private ViewPort viewPort;
private final Vector3f[] points = new Vector3f[8];
private final GeometryList shadowCasters = new GeometryList(new OpaqueComparator());
private final ListMap<Light, ShadowMap> shadowedLights = new ListMap<>();
private final RenderState prePassRenderState = RenderState.ADDITIONAL.clone();
private final MatParamOverride pointLightOverride = new MatParamOverride(VarType.Boolean, "IsPointLight", true);
private final TextureArray array = new TextureArray();
private int textureSize = 1024;
private int nextArraySlice = 0;
// parameters for directional lights
private final DirectionalShadowParameters directionalParams = new DirectionalShadowParameters();
public InPassShadowRenderer() {
for (int i = 0; i < points.length; i++) {
points[i] = new Vector3f();
}
prePassRenderState.setFaceCullMode(RenderState.FaceCullMode.Back);
prePassRenderState.setColorWrite(false);
prePassRenderState.setDepthWrite(true);
prePassRenderState.setDepthTest(true);
prePassRenderState.setPolyOffset(0, 0);
array.setAnisotropicFilter(1);
array.setShadowCompareMode(ShadowCompareMode.LessOrEqual);
array.setMagFilter(MagFilter.Bilinear);
array.setMinFilter(MinFilter.BilinearNoMipMaps);
}
public void displayDebug(AssetManager assetManager, Node guiRoot) {
guiRoot.addControl(new ShadowDebugControl(assetManager, this));
}
@Override
public void initialize(RenderManager rm, ViewPort vp) {
this.renderManager = rm;
this.viewPort = vp;
}
public DirectionalShadowParameters directional() {
return directionalParams;
}
public void setPolyOffset(float factor, float units) {
prePassRenderState.setPolyOffset(factor, units);
}
public int getTextureSize() {
return textureSize;
}
public void setTextureSize(int textureSize) {
// TODO: support changing texture size after shadow maps are created
this.textureSize = textureSize;
}
public TextureArray getShadowMapTexture() {
return array;
}
public void addLight(Light light) {
if (array.getImage() == null) {
array.setImage(new Image(
Format.Depth32F,
textureSize,
textureSize,
0,
new ArrayList<ByteBuffer>(),
ColorSpace.Linear));
}
ShadowMap shadowMap;
switch (light.getType()) {
case Directional:
shadowMap = new DirectionalArrayShadowMap(
(DirectionalLight) light,
array,
nextArraySlice,
textureSize,
directionalParams.getNumSplits());
break;
case Point:
shadowMap = new PointArrayShadowMap(
(PointLight) light,
array,
nextArraySlice,
textureSize);
break;
case Spot:
shadowMap = new SpotArrayShadowMap(
(SpotLight) light,
array,
nextArraySlice,
textureSize);
break;
default:
throw new UnsupportedOperationException();
}
shadowedLights.put(light, shadowMap);
nextArraySlice += shadowMap.getNumSlices();
}
@Override
public void reshape(ViewPort vp, int w, int h) {
}
@Override
public boolean isInitialized() {
return this.viewPort != null;
}
@Override
public void preFrame(float tpf) {
}
private void renderShadowMaps(ViewPort viewPort) {
renderManager.setForcedRenderState(prePassRenderState);
renderManager.setForcedTechnique(PRE_SHADOW_TECHNIQUE_NAME);
renderManager.addForcedMatParam(pointLightOverride);
for (int i = 0; i < shadowedLights.size(); i++) {
Light light = shadowedLights.getKey(i);
ShadowMap shadowMap = shadowedLights.getValue(i);
TempVars vars = TempVars.get();
try {
light.setFrustumCheckNeeded(false);
light.setIntersectsFrustum(light.intersectsFrustum(viewPort.getCamera(), vars));
if (!light.isIntersectsFrustum()) {
continue;
}
} finally {
vars.release();
}
pointLightOverride.setEnabled(shadowMap.getLightType() == Type.Point);
switch (shadowMap.getLightType()) {
case Directional:
DirectionalArrayShadowMap directionalShadow = (DirectionalArrayShadowMap) shadowMap;
directionalShadow.renderShadowMap(renderManager, viewPort, directionalParams, shadowCasters, points);
break;
case Point:
PointArrayShadowMap pointShadow = (PointArrayShadowMap) shadowMap;
pointShadow.renderShadowMap(renderManager, viewPort, shadowCasters);
break;
case Spot:
SpotArrayShadowMap spotShadow = (SpotArrayShadowMap) shadowMap;
spotShadow.renderShadowMap(renderManager, viewPort, shadowCasters);
break;
default:
throw new UnsupportedOperationException();
}
light.setShadowMap(shadowMap);
}
Renderer renderer = renderManager.getRenderer();
renderer.setFrameBuffer(viewPort.getOutputFrameBuffer());
renderManager.removeForcedMatParam(pointLightOverride);
renderManager.setForcedRenderState(null);
renderManager.setForcedTechnique(null);
renderManager.setCamera(viewPort.getCamera(), false);
}
@Override
public void postQueue(RenderQueue rq) {
directionalParams.updateSplitPositions(viewPort.getCamera());
renderShadowMaps(viewPort);
}
@Override
public void postFrame(FrameBuffer out) {
// TODO: call discard contents on all the framebuffers.
for (int i = 0; i < shadowedLights.size(); i++) {
Light light = shadowedLights.getKey(i);
light.setShadowMap(null);
}
}
@Override
public void cleanup() {
}
@Override
public void setProfiler(AppProfiler profiler) {
}
}

@ -0,0 +1,183 @@
/*
* Copyright (c) 2009-2016 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.next;
import com.jme3.shadow.next.pssm.DirectionalShadowParameters;
import com.jme3.light.DirectionalLight;
import com.jme3.light.Light;
import com.jme3.material.RenderState;
import com.jme3.math.Vector3f;
import com.jme3.post.SceneProcessor;
import com.jme3.profile.AppProfiler;
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.shadow.next.pssm.DirectionalShadowMap;
import com.jme3.texture.FrameBuffer;
import java.util.ArrayList;
import java.util.List;
/**
* The 4th generation of shadow mapping in jME3.
* <p>
* This version is primarily focused on rendering in-pass shadows, so pre-pass
* and subsequent stages are separated.
*
* @author Kirill Vainer
*/
public class PreShadowRenderer implements SceneProcessor {
private static final String PRE_SHADOW_TECHNIQUE_NAME = "PreShadow";
private RenderManager renderManager;
private ViewPort viewPort;
private final Vector3f[] points = new Vector3f[8];
private final GeometryList shadowCasters = new GeometryList(new OpaqueComparator());
private final List<ShadowMap> shadowMaps = new ArrayList<>();
private final RenderState prePassRenderState = RenderState.ADDITIONAL.clone();
private int textureSize = 1024;
// parameters for directional lights
private final DirectionalShadowParameters directionalParams = new DirectionalShadowParameters();
public PreShadowRenderer() {
for (int i = 0; i < points.length; i++) {
points[i] = new Vector3f();
}
prePassRenderState.setFaceCullMode(RenderState.FaceCullMode.Off);
prePassRenderState.setColorWrite(false);
prePassRenderState.setDepthWrite(true);
prePassRenderState.setDepthTest(true);
prePassRenderState.setPolyOffset(1.2f, 0);
}
@Override
public void initialize(RenderManager rm, ViewPort vp) {
this.renderManager = rm;
this.viewPort = vp;
}
public DirectionalShadowParameters directional() {
return directionalParams;
}
public void setPolyOffset(float factor, float units) {
// TODO: might want to set this separately per model
prePassRenderState.setPolyOffset(factor, units);
}
public int getTextureSize() {
return textureSize;
}
public void setTextureSize(int textureSize) {
// TODO: support changing texture size after shadow maps are created
this.textureSize = textureSize;
}
public void addLight(Light light) {
ShadowMap shadowMap;
switch (light.getType()) {
case Directional:
shadowMap = new DirectionalShadowMap(
(DirectionalLight) light,
textureSize,
directionalParams.getNumSplits(),
points);
break;
default:
throw new UnsupportedOperationException();
}
light.setShadowMap(shadowMap);
shadowMaps.add(shadowMap);
}
@Override
public void reshape(ViewPort vp, int w, int h) {
}
@Override
public boolean isInitialized() {
return this.viewPort != null;
}
@Override
public void preFrame(float tpf) {
}
private void renderShadowMaps() {
renderManager.setForcedRenderState(prePassRenderState);
renderManager.setForcedTechnique(PRE_SHADOW_TECHNIQUE_NAME);
for (ShadowMap shadowMap : shadowMaps) {
switch (shadowMap.getLightType()) {
case Directional:
DirectionalShadowMap directionalShadow = (DirectionalShadowMap) shadowMap;
directionalShadow.renderShadowMap(renderManager, viewPort, directionalParams, shadowCasters);
break;
default:
throw new UnsupportedOperationException();
}
}
Renderer renderer = renderManager.getRenderer();
renderer.setFrameBuffer(viewPort.getOutputFrameBuffer());
renderManager.setForcedRenderState(null);
renderManager.setForcedTechnique(null);
renderManager.setCamera(viewPort.getCamera(), false);
}
@Override
public void postQueue(RenderQueue rq) {
directionalParams.updateSplitPositions(viewPort.getCamera());
renderShadowMaps();
}
@Override
public void postFrame(FrameBuffer out) {
}
@Override
public void cleanup() {
}
@Override
public void setProfiler(AppProfiler profiler) {
}
}

@ -0,0 +1,98 @@
/*
* Copyright (c) 2009-2017 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.next;
import com.jme3.asset.AssetManager;
import com.jme3.material.Material;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.AbstractControl;
import com.jme3.texture.Image;
import com.jme3.texture.TextureArray;
import com.jme3.ui.Picture;
import java.util.ArrayList;
import java.util.List;
/**
* Shows the shadow maps on the screen
*
* @author Kirill Vainer
*/
final class ShadowDebugControl extends AbstractControl {
private final List<Picture> pictures = new ArrayList<>();
public ShadowDebugControl(AssetManager assetManager, InPassShadowRenderer shadowRenderer) {
TextureArray shadowMapArray = shadowRenderer.getShadowMapTexture();
Image shadowMap = shadowMapArray.getImage();
for (int i = 0; i < shadowMap.getDepth(); i++) {
Picture picture = new Picture("Shadow Map " + i);
picture.setPosition(20, i * 128 + 20);
picture.setWidth(128);
picture.setHeight(128);
Material material = new Material(assetManager, "Common/MatDefs/Shadow/ShowShadowArray.j3md");
material.setTexture("ShadowMapArray", shadowMapArray);
material.setFloat("ShadowMapSlice", i);
picture.setMaterial(material);
pictures.add(picture);
}
}
@Override
public void setSpatial(Spatial spatial) {
if (spatial != null) {
for (Picture picture : pictures) {
((Node) spatial).detachChild(picture);
}
}
super.setSpatial(spatial);
if (spatial != null) {
for (Picture picture : pictures) {
((Node) spatial).attachChild(picture);
}
}
}
@Override
protected void controlUpdate(float tpf) {
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
}
}

@ -0,0 +1,50 @@
/*
* Copyright (c) 2009-2016 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.next;
import com.jme3.light.Light;
import com.jme3.light.Light.Type;
/**
* Represents shadow information for a light.
* @param <T> Type of light
* @author Kirill Vainer
*/
public interface ShadowMap<T extends Light> {
public Type getLightType();
public int getNumSlices();
public ShadowMapSlice<T> getSlice(int index);
}

@ -0,0 +1,58 @@
/*
* Copyright (c) 2009-2016 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.next;
import com.jme3.light.Light;
import com.jme3.math.Matrix4f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.GeometryList;
/**
* Represents a single slice of a shadow map.
*
* @param <T> Type of light
*
* @author Kirill Vainer
*/
public interface ShadowMapSlice<T extends Light> {
public static final Matrix4f BIAS_MATRIX = new Matrix4f(
0.5f, 0.0f, 0.0f, 0.5f,
0.0f, 0.5f, 0.0f, 0.5f,
0.0f, 0.0f, 0.5f, 0.5f,
0.0f, 0.0f, 0.0f, 1.0f);
public Matrix4f getBiasedViewProjectionMatrix();
public void renderShadowMap(RenderManager renderManager, T light, ViewPort viewPort, GeometryList shadowCasters);
}

@ -0,0 +1,47 @@
/*
* Copyright (c) 2009-2016 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.next.array;
import com.jme3.shadow.next.ShadowMap;
import com.jme3.texture.TextureArray;
/**
* Represents shadow information for a light, uses texture arrays.
*
* @author Kirill Vainer
*/
public interface ArrayShadowMap extends ShadowMap {
public TextureArray getArray();
public int getFirstArraySlice();
}

@ -0,0 +1,41 @@
/*
* Copyright (c) 2009-2016 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.next.array;
import com.jme3.light.Light;
import com.jme3.shadow.next.ShadowMapSlice;
/**
* @author Kirill Vainer
*/
public interface ArrayShadowMapSlice<T extends Light> extends ShadowMapSlice<T> {
}

@ -0,0 +1,75 @@
/*
* Copyright (c) 2009-2016 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.next.array;
import com.jme3.light.Light;
import com.jme3.texture.TextureArray;
/**
*
* @author Kirill Vainer
*/
public abstract class BaseArrayShadowMap<T extends ArrayShadowMapSlice> implements ArrayShadowMap {
protected final TextureArray array;
protected final int firstArraySlice;
protected T[] slices;
public BaseArrayShadowMap(TextureArray array, int firstArraySlice) {
this.array = array;
this.firstArraySlice = firstArraySlice;
}
@Override
public TextureArray getArray() {
return array;
}
@Override
public int getFirstArraySlice() {
return firstArraySlice;
}
@Override
public abstract Light.Type getLightType();
@Override
public int getNumSlices() {
return slices.length;
}
@Override
public T getSlice(int index) {
return slices[index];
}
}

@ -0,0 +1,98 @@
/*
* Copyright (c) 2009-2016 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.next.array;
import com.jme3.light.Light;
import com.jme3.math.Matrix4f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.Renderer;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.GeometryList;
import com.jme3.texture.FrameBuffer;
import com.jme3.texture.Image;
import com.jme3.texture.TextureArray;
/**
* @param <T>
* @author Kirill Vainer
*/
public class BaseArrayShadowMapSlice<T extends Light> implements ArrayShadowMapSlice<T> {
protected final FrameBuffer frameBuffer;
protected final Camera shadowCamera;
protected final Matrix4f biasedViewProjectionMatrix = new Matrix4f();
protected boolean fbNeedClear = true;
public BaseArrayShadowMapSlice(TextureArray array, int layer, int textureSize, boolean useBorder) {
this.shadowCamera = new Camera(textureSize, textureSize);
if (useBorder) {
float onePx = 1f / textureSize;
this.shadowCamera.setViewPort(onePx, 1f - onePx, onePx, 1f - onePx);
}
this.frameBuffer = new FrameBuffer(textureSize, textureSize, 1);
Image image = array.getImage();
image.setDepth(image.getDepth() + 1);
image.addData(null);
this.frameBuffer.setDepthTexture(array, layer);
}
@Override
public Matrix4f getBiasedViewProjectionMatrix() {
return biasedViewProjectionMatrix;
}
@Override
public void renderShadowMap(RenderManager renderManager, Light light, ViewPort viewPort, GeometryList shadowCasters) {
Renderer renderer = renderManager.getRenderer();
if (fbNeedClear) {
renderer.setFrameBuffer(frameBuffer);
renderer.clearClipRect();
renderer.clearBuffers(false, true, false);
fbNeedClear = false;
}
if (shadowCasters.size() > 0) {
renderManager.setCamera(shadowCamera, false);
viewPort.getQueue().renderShadowQueue(shadowCasters, renderManager, shadowCamera, true);
fbNeedClear = true;
}
BIAS_MATRIX.mult(shadowCamera.getViewProjectionMatrix(), biasedViewProjectionMatrix);
}
}

@ -0,0 +1,81 @@
/*
* Copyright (c) 2009-2016 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.next.array;
import com.jme3.light.DirectionalLight;
import com.jme3.light.Light;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.GeometryList;
import com.jme3.shadow.next.pssm.DirectionalShadowParameters;
import com.jme3.texture.TextureArray;
/**
* @author Kirill Vainer
*/
public class DirectionalArrayShadowMap extends BaseArrayShadowMap<DirectionalArrayShadowMapSlice> {
private final DirectionalLight light;
private final Vector3f projectionSplitPositions = new Vector3f();
public DirectionalArrayShadowMap(DirectionalLight light, TextureArray array, int firstArraySlice, int textureSize, int numSplits) {
super(array, firstArraySlice);
this.light = light;
this.slices = new DirectionalArrayShadowMapSlice[numSplits];
for (int i = 0; i < numSplits; i++) {
this.slices[i] = new DirectionalArrayShadowMapSlice(array, firstArraySlice + i, textureSize);
}
}
public void renderShadowMap(RenderManager renderManager, ViewPort viewPort, DirectionalShadowParameters params, GeometryList shadowCasters, Vector3f[] points) {
projectionSplitPositions.set(params.getProjectionSplitPositions());
float[] splitPositionsViewSpace = params.getSplitPositions();
for (int i = 0; i < slices.length; i++) {
float near = splitPositionsViewSpace[i];
float far = splitPositionsViewSpace[i + 1];
shadowCasters.clear();
slices[i].updateShadowCamera(viewPort, light, shadowCasters, near, far, points);
slices[i].renderShadowMap(renderManager, light, viewPort, shadowCasters);
}
}
public Vector3f getProjectionSplitPositions() {
return projectionSplitPositions;
}
@Override
public Light.Type getLightType() {
return Light.Type.Directional;
}
}

@ -0,0 +1,75 @@
/*
* Copyright (c) 2009-2016 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.next.array;
import com.jme3.light.DirectionalLight;
import com.jme3.math.Vector3f;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.GeometryList;
import com.jme3.shadow.ShadowUtil;
import com.jme3.texture.TextureArray;
/**
* @author Kirill Vainer
*/
public class DirectionalArrayShadowMapSlice extends BaseArrayShadowMapSlice<DirectionalLight> {
public DirectionalArrayShadowMapSlice(TextureArray array, int layer, int textureSize) {
super(array, layer, textureSize, true);
this.shadowCamera.setParallelProjection(true);
}
private static boolean isParallelToYUp(Vector3f direction) {
return direction.x == 0 && direction.z == 0
&& (direction.y == -1 || direction.y == 1);
}
public void updateShadowCamera(
ViewPort viewPort,
DirectionalLight light,
GeometryList shadowCasters,
float near,
float far,
Vector3f[] points) {
if (isParallelToYUp(light.getDirection())) {
// direction and up cannot be parallel
shadowCamera.lookAtDirection(light.getDirection(), Vector3f.UNIT_Z);
} else {
shadowCamera.lookAtDirection(light.getDirection(), Vector3f.UNIT_Y);
}
int textureSize = frameBuffer.getWidth();
ShadowUtil.updateFrustumPoints(viewPort.getCamera(), near, far, points);
ShadowUtil.updateShadowCamera(viewPort, null, shadowCamera, points, shadowCasters, textureSize);
}
}

@ -0,0 +1,99 @@
/*
* Copyright (c) 2009-2017 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.next.array;
import com.jme3.light.Light;
import com.jme3.light.Light.Type;
import com.jme3.light.PointLight;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.GeometryList;
import com.jme3.texture.TextureArray;
/**
* @author Kirill Vainer
*/
public class PointArrayShadowMap extends BaseArrayShadowMap<PointArrayShadowMapSlice> {
private final PointLight light;
private static final Quaternion[] ROTATIONS = new Quaternion[6];
static {
for (int i = 0; i < ROTATIONS.length; i++) {
ROTATIONS[i] = new Quaternion();
}
// left
ROTATIONS[0].fromAxes(Vector3f.UNIT_Z, Vector3f.UNIT_Y, Vector3f.UNIT_X.mult(-1f));
// right
ROTATIONS[1].fromAxes(Vector3f.UNIT_Z.mult(-1f), Vector3f.UNIT_Y, Vector3f.UNIT_X);
// bottom
ROTATIONS[2].fromAxes(Vector3f.UNIT_X.mult(-1f), Vector3f.UNIT_Z.mult(-1f), Vector3f.UNIT_Y.mult(-1f));
// top
ROTATIONS[3].fromAxes(Vector3f.UNIT_X.mult(-1f), Vector3f.UNIT_Z, Vector3f.UNIT_Y);
// forward
ROTATIONS[4].fromAxes(Vector3f.UNIT_X.mult(-1f), Vector3f.UNIT_Y, Vector3f.UNIT_Z.mult(-1f));
// backward
ROTATIONS[5].fromAxes(Vector3f.UNIT_X, Vector3f.UNIT_Y, Vector3f.UNIT_Z);
}
public PointArrayShadowMap(PointLight light, TextureArray array, int firstArraySlice, int textureSize) {
super(array, firstArraySlice);
this.light = light;
this.slices = new PointArrayShadowMapSlice[6];
for (int i = 0; i < slices.length; i++) {
this.slices[i] = new PointArrayShadowMapSlice(array, firstArraySlice + i, textureSize, ROTATIONS[i]);
}
}
public void renderShadowMap(RenderManager renderManager, ViewPort viewPort, GeometryList shadowCasters) {
for (int i = 0; i < slices.length; i++) {
shadowCasters.clear();
slices[i].updateShadowCamera(viewPort, light, shadowCasters);
slices[i].renderShadowMap(renderManager, light, viewPort, shadowCasters);
}
}
@Override
public Light.Type getLightType() {
return Type.Point;
}
}

@ -0,0 +1,60 @@
/*
* Copyright (c) 2009-2017 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.next.array;
import com.jme3.light.PointLight;
import com.jme3.math.Quaternion;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.GeometryList;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.Spatial;
import com.jme3.shadow.ShadowUtil;
import com.jme3.texture.TextureArray;
/**
* @author Kirill Vainer
*/
public class PointArrayShadowMapSlice extends BaseArrayShadowMapSlice<PointLight> {
public PointArrayShadowMapSlice(TextureArray array, int layer, int textureSize, Quaternion axes) {
super(array, layer, textureSize, false);
shadowCamera.setAxes(axes);
}
public void updateShadowCamera(ViewPort viewPort, PointLight light, GeometryList shadowCasters) {
shadowCamera.setFrustumPerspective(90f, 1f, 0.5f, light.getRadius());
shadowCamera.setLocation(light.getPosition());
for (Spatial scene : viewPort.getScenes()) {
ShadowUtil.getGeometriesInCamFrustum(scene, shadowCamera, ShadowMode.Cast, shadowCasters);
}
}
}

@ -0,0 +1,66 @@
/*
* Copyright (c) 2009-2016 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.next.array;
import com.jme3.light.Light;
import com.jme3.light.SpotLight;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.GeometryList;
import com.jme3.texture.TextureArray;
/**
* @author Kirill Vainer
*/
public class SpotArrayShadowMap extends BaseArrayShadowMap<SpotArrayShadowMapSlice> {
private final SpotLight light;
public SpotArrayShadowMap(SpotLight light, TextureArray array, int firstArraySlice, int textureSize) {
super(array, firstArraySlice);
this.light = light;
slices = new SpotArrayShadowMapSlice[]{
new SpotArrayShadowMapSlice(array, firstArraySlice, textureSize)
};
}
public void renderShadowMap(RenderManager renderManager, ViewPort viewPort, GeometryList shadowCasters) {
shadowCasters.clear();
slices[0].updateShadowCamera(viewPort, light, shadowCasters);
slices[0].renderShadowMap(renderManager, light, viewPort, shadowCasters);
}
@Override
public Light.Type getLightType() {
return Light.Type.Spot;
}
}

@ -0,0 +1,71 @@
/*
* Copyright (c) 2009-2016 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.next.array;
import com.jme3.light.SpotLight;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.GeometryList;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Spatial;
import com.jme3.shadow.ShadowUtil;
import com.jme3.texture.TextureArray;
/**
* @author Kirill Vainer
*/
public class SpotArrayShadowMapSlice extends BaseArrayShadowMapSlice<SpotLight> {
public SpotArrayShadowMapSlice(TextureArray array, int layer, int textureSize) {
super(array, layer, textureSize, true);
}
private static boolean isParallelToYUp(Vector3f direction) {
return direction.x == 0 && direction.z == 0
&& (direction.y == -1 || direction.y == 1);
}
public void updateShadowCamera(ViewPort viewPort, SpotLight light, GeometryList shadowCasters) {
shadowCamera.setLocation(light.getPosition());
if (isParallelToYUp(light.getDirection())) {
// direction and up cannot be parallel
shadowCamera.lookAtDirection(light.getDirection(), Vector3f.UNIT_Z);
} else {
shadowCamera.lookAtDirection(light.getDirection(), Vector3f.UNIT_Y);
}
shadowCamera.setFrustumPerspective(light.getSpotOuterAngle() * FastMath.RAD_TO_DEG * 2.0f, 1, 1, light.getSpotRange());
for (Spatial scene : viewPort.getScenes()) {
ShadowUtil.getGeometriesInCamFrustum(scene, shadowCamera, RenderQueue.ShadowMode.Cast, shadowCasters);
}
}
}

@ -0,0 +1,87 @@
/*
* Copyright (c) 2009-2016 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.next.pssm;
import com.jme3.light.Light;
import com.jme3.math.Matrix4f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.Renderer;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.GeometryList;
import com.jme3.texture.FrameBuffer;
import com.jme3.texture.Image;
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.shadow.next.ShadowMapSlice;
public abstract class BaseShadowMapSlice<T extends Light> implements ShadowMapSlice<T> {
protected final FrameBuffer frameBuffer;
protected final Texture2D depthTexture;
protected final Camera shadowCamera;
protected final Vector3f[] points;
protected final Matrix4f biasedViewProjectionMatrix = new Matrix4f();
public BaseShadowMapSlice(int size, Vector3f[] points) {
this.depthTexture = new Texture2D(size, size, Image.Format.Depth16);
this.depthTexture.setAnisotropicFilter(1);
this.depthTexture.setShadowCompareMode(ShadowCompareMode.LessOrEqual);
this.depthTexture.setMagFilter(MagFilter.Bilinear);
this.depthTexture.setMinFilter(MinFilter.BilinearNoMipMaps);
this.shadowCamera = new Camera(size, size);
this.frameBuffer = new FrameBuffer(size, size, 1);
this.frameBuffer.setDepthTexture(depthTexture);
this.points = points;
}
@Override
public void renderShadowMap(RenderManager renderManager, T light, ViewPort viewPort, GeometryList shadowCasters) {
Renderer renderer = renderManager.getRenderer();
renderer.setFrameBuffer(frameBuffer);
renderer.clearBuffers(false, true, false);
if (shadowCasters.size() > 0) {
renderManager.setCamera(shadowCamera, false);
viewPort.getQueue().renderShadowQueue(shadowCasters, renderManager, shadowCamera, true);
}
}
@Override
public Matrix4f getBiasedViewProjectionMatrix() {
return BIAS_MATRIX.mult(shadowCamera.getViewProjectionMatrix(), biasedViewProjectionMatrix);
}
}

@ -0,0 +1,92 @@
/*
* Copyright (c) 2009-2016 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.next.pssm;
import com.jme3.light.DirectionalLight;
import com.jme3.light.Light.Type;
import com.jme3.math.Vector3f;
import com.jme3.math.Vector4f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.GeometryList;
import com.jme3.shadow.next.ShadowMapSlice;
import com.jme3.shadow.next.ShadowMap;
/**
* @author Kirill Vainer
*/
public class DirectionalShadowMap implements ShadowMap<DirectionalLight> {
private final DirectionalLight light;
private final DirectionalShadowMapSlice[] splits;
private final Vector3f projectionSplitPositions = new Vector3f();
public DirectionalShadowMap(DirectionalLight light, int textureSize, int numSplits, Vector3f[] points) {
this.light = light;
this.splits = new DirectionalShadowMapSlice[numSplits];
for (int i = 0; i < splits.length; i++) {
this.splits[i] = new DirectionalShadowMapSlice(textureSize, points);
}
}
public void renderShadowMap(RenderManager renderManager, ViewPort viewPort, DirectionalShadowParameters params, GeometryList shadowCasters) {
projectionSplitPositions.set(params.getProjectionSplitPositions());
float[] splitPositionsViewSpace = params.getSplitPositions();
for (int i = 0; i < splits.length; i++) {
float near = splitPositionsViewSpace[i];
float far = splitPositionsViewSpace[i + 1];
shadowCasters.clear();
splits[i].updateShadowCamera(viewPort, light, shadowCasters, near, far);
splits[i].renderShadowMap(renderManager, light, viewPort, shadowCasters);
}
}
public Vector3f getProjectionSplitPositions() {
return projectionSplitPositions;
}
@Override
public int getNumSlices() {
return splits.length;
}
@Override
public ShadowMapSlice getSlice(int index) {
return splits[index];
}
@Override
public Type getLightType() {
return Type.Directional;
}
}

@ -0,0 +1,67 @@
/*
* Copyright (c) 2009-2016 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.next.pssm;
import com.jme3.light.DirectionalLight;
import com.jme3.math.Vector3f;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.GeometryList;
import com.jme3.shadow.ShadowUtil;
import com.jme3.texture.Texture2D;
/**
* @author Kirill Vainer
*/
public class DirectionalShadowMapSlice extends BaseShadowMapSlice<DirectionalLight> {
public DirectionalShadowMapSlice(int size, Vector3f[] points) {
super(size, points);
this.shadowCamera.setParallelProjection(true);
}
public void updateShadowCamera(
ViewPort viewPort,
DirectionalLight light,
GeometryList shadowCasters,
float near,
float far) {
ShadowUtil.updateFrustumPoints(viewPort.getCamera(), near, far, points);
shadowCamera.lookAtDirection(light.getDirection(), shadowCamera.getUp());
int textureSize = frameBuffer.getWidth();
ShadowUtil.updateShadowCamera(viewPort, null, shadowCamera, points, shadowCasters, textureSize);
}
public Texture2D getTexture() {
return depthTexture;
}
}

@ -0,0 +1,139 @@
/*
* Copyright (c) 2009-2016 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.next.pssm;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.shadow.PssmShadowUtil;
/**
* @author Kirill Vainer
*/
public final class DirectionalShadowParameters {
private float lambda = 0.65f;
private int numSplits = 4;
protected float zFarOverride = 0;
private float[] splitPositions = new float[numSplits + 1];
private final Vector3f projectionSplitPositions = new Vector3f();
public float getLambda() {
return lambda;
}
public void setLambda(float lambda) {
this.lambda = lambda;
}
public int getNumSplits() {
return numSplits;
}
public void setNumSplits(int numSplits) {
if (numSplits < 1 || numSplits > 4) {
throw new IllegalArgumentException("Number of splits must be between 1 and 4");
}
this.numSplits = numSplits;
this.splitPositions = new float[numSplits + 1];
}
public float[] getSplitPositions() {
return splitPositions;
}
public Vector3f getProjectionSplitPositions() {
return projectionSplitPositions;
}
/**
* 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.
*
* The default value is dynamically computed based on 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) {
this.zFarOverride = zFar;
// TODO: Fade length not supported yet
// if (zFarOverride == 0) {
// fadeInfo = null;
// frustumCam = null;
// } else {
// if (fadeInfo != null) {
// fadeInfo.set(zFarOverride - fadeLength, 1f / fadeLength);
// }
// if (frustumCam == null && viewPort != null) {
// initFrustumCam();
// }
// }
}
public void updateSplitPositions(Camera viewCamera) {
float near = viewCamera.getFrustumNear();
float far = zFarOverride == 0f ? viewCamera.getFrustumFar() : zFarOverride;
PssmShadowUtil.updateFrustumSplits(splitPositions, near, far, lambda);
// TODO: Parallel projection can have negative near value, so split
// positions must be adjusted.
// if (viewCamera.isParallelProjection()) {
// for (int i = 0; i < splitPositions.length; i++) {
// splitPositions[i] = splitPositions[i] / (far - near);
// }
// }
switch (splitPositions.length) {
case 5:
// projectionSplitPositions.w = 1.0f;
case 4:
projectionSplitPositions.z = viewCamera.getViewToProjectionZ(splitPositions[3]);
case 3:
projectionSplitPositions.y = viewCamera.getViewToProjectionZ(splitPositions[2]);
case 2:
case 1:
projectionSplitPositions.x = viewCamera.getViewToProjectionZ(splitPositions[1]);
break;
}
}
}

@ -173,7 +173,7 @@ void main(){
// allow use of control flow
if(g_LightDirection.w != 0.0){
#endif
spotFallOff = computeSpotFalloff(g_LightDirection, lightVec);
spotFallOff = computeSpotFalloff(g_LightDirection, lightDir.xyz);
#if __VERSION__ >= 110
if(spotFallOff <= 0.0){
gl_FragColor.rgb = AmbientSum * diffuseColor.rgb;

@ -27,6 +27,9 @@ MaterialDef Phong Lighting {
// Specular power/shininess
Float Shininess : 1
// Ambient map
Texture2D AmbientMap
// Diffuse map
Texture2D DiffuseMap
@ -79,7 +82,7 @@ MaterialDef Phong Lighting {
Boolean EnvMapAsSphereMap
//shadows
Int FilterMode
Int FilterMode
Boolean HardwareShadows
Texture2D ShadowMap0
@ -120,6 +123,9 @@ MaterialDef Phong Lighting {
Boolean UseInstancing
Boolean BackfaceShadows : false
// PreShadow: use point light mode for depth
Boolean IsPointLight
}
Technique {
@ -135,13 +141,13 @@ MaterialDef Phong Lighting {
ViewMatrix
CameraPosition
WorldMatrix
ViewProjectionMatrix
ViewProjectionMatrix
}
Defines {
Defines {
VERTEX_COLOR : UseVertexColor
VERTEX_LIGHTING : VertexLighting
MATERIAL_COLORS : UseMaterialColors
VERTEX_LIGHTING : VertexLighting
MATERIAL_COLORS : UseMaterialColors
DIFFUSEMAP : DiffuseMap
NORMALMAP : NormalMap
SPECULARMAP : SpecularMap
@ -154,8 +160,8 @@ MaterialDef Phong Lighting {
SEPARATE_TEXCOORD : SeparateTexCoord
DISCARD_ALPHA : AlphaDiscardThreshold
USE_REFLECTION : EnvMap
SPHERE_MAP : EnvMapAsSphereMap
NUM_BONES : NumberOfBones
SPHERE_MAP : EnvMapAsSphereMap
NUM_BONES : NumberOfBones
INSTANCING : UseInstancing
NUM_MORPH_TARGETS: NumberOfMorphTargets
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
@ -213,12 +219,16 @@ MaterialDef Phong Lighting {
WorldViewMatrix
ViewProjectionMatrix
ViewMatrix
WorldMatrix
CameraPosition
FrustumNearFar
}
Defines {
DISCARD_ALPHA : AlphaDiscardThreshold
NUM_BONES : NumberOfBones
INSTANCING : UseInstancing
POINT_LIGHT : IsPointLight
NUM_MORPH_TARGETS: NumberOfMorphTargets
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
}

@ -168,7 +168,7 @@ void main(){
// allow use of control flow
if(lightColor.w > 1.0){
#endif
spotFallOff = computeSpotFalloff(g_LightDirection, lightVec);
spotFallOff = computeSpotFalloff(g_LightDirection, vLightDir.xyz);
#if __VERSION__ >= 110
}
#endif

@ -2,6 +2,7 @@
#import "Common/ShaderLib/PBR.glsllib"
#import "Common/ShaderLib/Parallax.glsllib"
#import "Common/ShaderLib/Lighting.glsllib"
#import "Common/ShaderLib/InPassShadows.glsllib"
varying vec2 texCoord;
#ifdef SEPARATE_TEXCOORD
@ -35,6 +36,8 @@ varying vec3 wPosition;
uniform mat4 g_LightProbeData3;
#endif
uniform vec4 g_AmbientLightColor;
#ifdef BASECOLORMAP
uniform sampler2D m_BaseColorMap;
#endif
@ -94,7 +97,7 @@ varying vec3 wNormal;
uniform float m_AlphaDiscardThreshold;
#endif
void main(){
void main() {
vec2 newTexCoord;
vec3 viewDir = normalize(g_CameraPosition - wPosition);
@ -223,11 +226,20 @@ void main(){
specularColor.rgb *= lightMapColor;
#endif
Shadow_ProcessPssmSlice();
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];
float shadowMapIndex = -1.0;
if (lightColor.w < 0.0) {
shadowMapIndex = floor(-lightColor.w);
lightColor.w = fract(-lightColor.w);
}
vec4 lightData1 = g_LightData[i+1];
vec4 lightDir;
vec3 lightVec;
lightComputeDir(wPosition, lightColor.w, lightData1, lightDir, lightVec);
@ -235,16 +247,19 @@ void main(){
float fallOff = 1.0;
#if __VERSION__ >= 110
// allow use of control flow
if(lightColor.w > 1.0){
if(lightColor.w > 0.4){
#endif
fallOff = computeSpotFalloff(g_LightData[i+2], lightVec);
fallOff = computeSpotFalloff(g_LightData[i+2], lightDir.xyz);
#if __VERSION__ >= 110
}
#endif
//point light attenuation
fallOff *= lightDir.w;
lightDir.xyz = normalize(lightDir.xyz);
if (shadowMapIndex >= 0.0) {
fallOff *= Shadow_Process(i / 3, lightColor.w, shadowMapIndex, lightVec, lightDir.xyz, wPosition, lightData1.w);
}
vec3 directDiffuse;
vec3 directSpecular;
@ -257,6 +272,8 @@ void main(){
gl_FragColor.rgb += directLighting * fallOff;
}
gl_FragColor.rgb += g_AmbientLightColor.rgb * diffuseColor.rgb;
#if NB_PROBES >= 1
vec3 color1 = vec3(0.0);
vec3 color2 = vec3(0.0);
@ -295,7 +312,6 @@ void main(){
weight3 /= weightSum;
#endif
gl_FragColor.rgb += color1 * clamp(weight1,0.0,1.0) + color2 * clamp(weight2,0.0,1.0) + color3 * clamp(weight3,0.0,1.0);
#endif
#if defined(EMISSIVE) || defined (EMISSIVEMAP)

@ -118,6 +118,9 @@ MaterialDef PBR Lighting {
Boolean UseVertexColor
Boolean BackfaceShadows : false
// PreShadow: use point light mode for depth
Boolean IsPointLight
}
Technique {
@ -175,12 +178,16 @@ MaterialDef PBR Lighting {
WorldViewMatrix
ViewProjectionMatrix
ViewMatrix
WorldMatrix
CameraPosition
FrustumNearFar
}
Defines {
DISCARD_ALPHA : AlphaDiscardThreshold
NUM_BONES : NumberOfBones
INSTANCING : UseInstancing
POINT_LIGHT : IsPointLight
NUM_MORPH_TARGETS: NumberOfMorphTargets
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
}

@ -1,6 +1,7 @@
#import "Common/ShaderLib/GLSLCompat.glsllib"
#import "Common/ShaderLib/Instancing.glsllib"
#import "Common/ShaderLib/Skinning.glsllib"
#import "Common/ShaderLib/InPassShadows.glsllib"
#import "Common/ShaderLib/MorphAnim.glsllib"
uniform vec4 m_BaseColor;
@ -59,7 +60,11 @@ void main(){
texCoord2 = inTexCoord2;
#endif
wPosition = TransformWorld(modelSpacePos).xyz;
vec3 worldPos = TransformWorld(modelSpacePos).xyz;
Shadow_ProcessProjCoord(worldPos);
wPosition = worldPos;
wNormal = TransformWorldNormal(modelSpaceNorm);
#if defined(NORMALMAP) || defined(PARALLAXMAP)

@ -188,7 +188,7 @@ void main(){
// allow use of control flow
if(lightColor.w > 1.0){
#endif
spotFallOff = computeSpotFalloff(g_LightData[i+2], lightVec);
spotFallOff = computeSpotFalloff(g_LightData[i+2], lightDir.xyz);
#if __VERSION__ >= 110
}
#endif

@ -165,7 +165,7 @@ void main(){
if(lightColor.w > 1.0){
#endif
vec4 lightDirection = g_LightData[i+2];
spotFallOff = computeSpotFalloff(lightDirection, lightVec);
spotFallOff = computeSpotFalloff(lightDirection, lightDir.xyz);
#if __VERSION__ >= 110
}
#endif

@ -14,6 +14,9 @@ MaterialDef Point Sprite {
Texture2D GlowMap
// The glow color of the object
Color GlowColor
// PreShadow: use point light mode for depth
Boolean IsPointLight
}
Technique {
@ -56,10 +59,14 @@ MaterialDef Point Sprite {
WorldViewMatrix
ViewProjectionMatrix
ViewMatrix
WorldMatrix
CameraPosition
FrustumNearFar
}
Defines {
COLOR_MAP : Texture
POINT_LIGHT : IsPointLight
}
ForcedRenderState {

@ -59,6 +59,9 @@ MaterialDef Unshaded {
Float ShadowMapSize
Boolean BackfaceShadows: true
// PreShadow: use point light mode for depth
Boolean IsPointLight
}
Technique {
@ -117,6 +120,9 @@ MaterialDef Unshaded {
WorldViewMatrix
ViewProjectionMatrix
ViewMatrix
WorldMatrix
CameraPosition
FrustumNearFar
}
Defines {
@ -124,6 +130,7 @@ MaterialDef Unshaded {
DISCARD_ALPHA : AlphaDiscardThreshold
NUM_BONES : NumberOfBones
INSTANCING : UseInstancing
POINT_LIGHT : IsPointLight
NUM_MORPH_TARGETS: NumberOfMorphTargets
NUM_TARGETS_BUFFERS: NumberOfTargetsBuffers
}

@ -1,11 +1,24 @@
MaterialDef Pre Shadow {
MaterialParameters {
// PreShadow: use point light mode for depth
Boolean IsPointLight
}
Technique {
VertexShader GLSL100 GLSL150 : Common/MatDefs/Shadow/PreShadow.vert
FragmentShader GLSL100 GLSL150 : Common/MatDefs/Shadow/PreShadow.frag
Defines {
POINT_LIGHT : IsPointLight
}
WorldParameters {
WorldViewProjectionMatrix
WorldViewMatrix
WorldMatrix
CameraPosition
FrustumNearFar
}
RenderState {

@ -4,20 +4,40 @@
#import "Common/ShaderLib/MorphAnim.glsllib"
attribute vec3 inPosition;
#ifdef DISCARD_ALPHA
attribute vec2 inTexCoord;
varying vec2 texCoord;
#endif
#ifdef POINT_LIGHT
uniform vec3 g_CameraPosition;
uniform vec2 g_FrustumNearFar;
#endif
void main(){
void main() {
vec4 modelSpacePos = vec4(inPosition, 1.0);
#ifdef NUM_MORPH_TARGETS
Morph_Compute(modelSpacePos, modelSpaceNorm);
#endif
#ifdef NUM_MORPH_TARGETS
Morph_Compute(modelSpacePos, modelSpaceNorm);
#endif
#ifdef NUM_BONES
Skinning_Compute(modelSpacePos);
#endif
#ifdef DISCARD_ALPHA
texCoord = inTexCoord;
#endif
#ifdef NUM_BONES
Skinning_Compute(modelSpacePos);
#endif
gl_Position = TransformWorldViewProjection(modelSpacePos);
texCoord = inTexCoord;
#ifdef POINT_LIGHT
vec3 lightDir = g_CameraPosition - TransformWorld(modelSpacePos).xyz;
// The Z value to write into the depth map, should be [0.0, 1.0]
float z = sqrt(length(lightDir) / g_FrustumNearFar.y);
// Remap [0.0, 1.0] into [-1.0, 1.0]
gl_Position.z = (clamp(z, 0.0, 1.0) * 2.0 - 1.0) * gl_Position.w;
#endif
}

@ -0,0 +1,15 @@
#import "Common/ShaderLib/GLSLCompat.glsllib"
uniform float m_ShadowMapSlice;
uniform sampler2DArray m_ShadowMapArray;
varying vec2 texCoord1;
void main() {
float shadow = texture2D(m_ShadowMapArray, vec3(texCoord1, m_ShadowMapSlice)).r;
shadow = sqrt(shadow);
// TODO: make it betterer
gl_FragColor.rgb = vec3(shadow);
gl_FragColor.a = 1.0;
}

@ -0,0 +1,17 @@
MaterialDef Pre Shadow {
MaterialParameters {
TextureArray ShadowMapArray
Float ShadowMapSlice
}
Technique {
VertexShader GLSL150 : Common/MatDefs/Misc/Unshaded.vert
FragmentShader GLSL150 : Common/MatDefs/Shadow/ShowShadowArray.frag
WorldParameters {
WorldViewProjectionMatrix
ViewProjectionMatrix
}
}
}

@ -25,6 +25,7 @@ out vec4 outFragColor;
# define texture3D texture
# define textureCube texture
# define texture2DLod textureLod
# define shadow2D(a,b) vec4(texture(a,b))
# define textureCubeLod textureLod
# if defined VERTEX_SHADER
# define varying out

@ -0,0 +1,165 @@
#import "Common/ShaderLib/GLSLCompat.glsllib"
#extension GL_EXT_texture_array : enable
#ifndef NUM_PSSM_SPLITS
#define NUM_PSSM_SPLITS 0
#endif
#ifdef IN_PASS_SHADOWS
uniform mat4 g_ShadowMatrices[(NB_LIGHTS/3) + NUM_PSSM_SPLITS];
#if NUM_PSSM_SPLITS > 0
varying vec3 dirProjCoord[NUM_PSSM_SPLITS];
#else
varying vec3 dirProjCoord[1];
#endif
#ifdef VERTEX_SHADER
void Shadow_ProcessProjCoord(vec3 worldPos) {
#if NUM_PSSM_SPLITS > 0
for (int i = 0; i < NUM_PSSM_SPLITS; i++) {
#if __VERSION__ >= 150
dirProjCoord[i] = mat4x3(g_ShadowMatrices[i]) * vec4(worldPos, 1.0);
#else
dirProjCoord[i] = (g_ShadowMatrices[i] * vec4(worldPos, 1.0)).xyz;
#endif
}
#endif
}
#else
uniform sampler2DArrayShadow g_ShadowMapArray;
uniform vec3 g_PssmSplits;
float pssmSliceOffset;
void Shadow_ProcessPssmSlice() {
#if NUM_PSSM_SPLITS > 1
pssmSliceOffset = dot(step(g_PssmSplits.xyz, gl_FragCoord.zzz), vec3(1.0));
#else
pssmSliceOffset = 0.0;
#endif
}
vec3 Shadow_GetCubeMapTC(in vec3 direction) {
vec3 axis = abs(direction);
float largest = max(axis.x, max(axis.y, axis.z));
vec3 tc;
if (largest == axis.x) {
if (direction.x > 0.0) {
tc = vec3( direction.z, -direction.y, 0.0);
} else {
tc = vec3(-direction.z, -direction.y, 1.0);
}
} else if (largest == axis.y) {
if (direction.y > 0.0) {
tc = vec3(-direction.x, direction.z, 2.0);
} else {
tc = vec3(-direction.x, -direction.z, 3.0);
}
} else {
if (direction.z > 0.0) {
tc = vec3(-direction.x, -direction.y, 4.0);
} else {
tc = vec3(direction.x, -direction.y, 5.0);
}
}
largest = 1.0 / largest;
tc.xy = 0.5 * (tc.xy * vec2(largest) + 1.0);
return tc;
}
float Shadow_Process(int lightIndex, float lightType, float shadowMapIndex,
vec3 lightVec, vec3 lightDir,
vec3 worldPos, float invRadius) {
vec4 tc;
if (lightType <= 0.2) {
vec3 projCoord = dirProjCoord[int(pssmSliceOffset)];
tc = vec4(projCoord.xy, shadowMapIndex + pssmSliceOffset, projCoord.z);
} else if (lightType <= 0.3) {
vec3 projCoord = Shadow_GetCubeMapTC(lightVec.xyz);
float dist = sqrt(length(lightVec) * invRadius);
tc = vec4(projCoord.xy, shadowMapIndex + projCoord.z, dist);
} else {
tc = g_ShadowMatrices[NUM_PSSM_SPLITS + lightIndex] * vec4(worldPos, 1.0);
tc.xyz /= tc.w;
tc = vec4(tc.xy, shadowMapIndex, tc.z);
}
#if __VERSION__ >= 150
return texture(g_ShadowMapArray, tc);
#else
return shadow2DArray(g_ShadowMapArray, tc).x;
#endif
}
#endif
#elif NUM_PSSM_SPLITS > 0
// A lightweight version of in-pass lighting that only handles directional lights
// Control flow and loop iteration count are static
varying vec4 vProjCoord[NUM_PSSM_SPLITS];
#ifdef VERTEX_SHADER
uniform mat4 g_DirectionalShadowMatrix[NUM_PSSM_SPLITS];
void Shadow_ProcessProjCoord(vec3 worldPos) {
for (int i = 0; i < NUM_PSSM_SPLITS; i++) {
vProjCoord[i] = g_DirectionalShadowMatrix[i] * vec4(worldPos, 1.0);
}
}
#else
uniform sampler2DShadow g_DirectionalShadowMap[NUM_PSSM_SPLITS];
uniform vec4 g_PssmSplits;
const vec2 invTexSize = vec2(1.0 / 1024.0);
float Shadow_SampleOffset(sampler2DShadow shadowMap, vec4 projCoord, vec2 offset) {
return shadow2D(shadowMap, vec3(projCoord.xy + offset * invTexSize, projCoord.z)).r;
}
float Shadow_Sample(sampler2DShadow shadowMap, vec4 projCoord) {
return shadow2D(shadowMap, projCoord.xyz).r;
}
#define GET_SHADOW(i) if (z < g_PssmSplits[i]) return Shadow_Sample(g_DirectionalShadowMap[i], vProjCoord[i]);
void Shadow_ProcessPssmSlice() {
}
float Shadow_ProcessDirectional() {
float z = gl_FragCoord.z;
GET_SHADOW(0);
#if NUM_PSSM_SPLITS > 1
GET_SHADOW(1)
#if NUM_PSSM_SPLITS > 2
GET_SHADOW(2)
#if NUM_PSSM_SPLITS > 3
GET_SHADOW(3)
#endif
#endif
#endif
return 1.0;
}
#endif
#else
#define NUM_PSSM_SPLITS 0
const int pssmSliceOffset = 0;
void Shadow_ProcessProjCoord(vec3 worldPos) {
}
void Shadow_ProcessPssmSlice() {
}
float Shadow_Process(int lightIndex, float lightType, float shadowMapIndex,
vec3 lightVec, vec3 lightDir,
vec3 worldPos, float invRadius) {
return 1.0;
}
#endif

@ -7,7 +7,7 @@
* Outputs the light direction and the light half vector.
*/
void lightComputeDir(in vec3 worldPos, in float lightType, in vec4 position, out vec4 lightDir, out vec3 lightVec){
float posLight = step(0.5, lightType);
float posLight = step(0.2, lightType);
vec3 tempVec = position.xyz * sign(posLight - 0.5) - (worldPos * posLight);
lightVec = tempVec;
float dist = length(tempVec);
@ -23,9 +23,9 @@ void lightComputeDir(in vec3 worldPos, in float lightType, in vec4 position, out
/*
* Computes the spot falloff for a spotlight
*/
float computeSpotFalloff(in vec4 lightDirection, in vec3 lightVector){
vec3 L=normalize(lightVector);
vec3 spotdir = normalize(lightDirection.xyz);
float computeSpotFalloff(in vec4 lightDirection, in vec3 lightVector) {
vec3 L = lightVector;
vec3 spotdir = lightDirection.xyz;
float curAngleCos = dot(-L, spotdir);
float innerAngleCos = floor(lightDirection.w) * 0.001;
float outerAngleCos = fract(lightDirection.w);

@ -39,7 +39,6 @@ import com.jme3.material.RenderState.BlendMode;
import com.jme3.material.RenderState.FaceCullMode;
import com.jme3.material.TechniqueDef.LightMode;
import com.jme3.material.TechniqueDef.ShadowMode;
import com.jme3.material.logic.StaticPassLightingLogic;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
@ -645,9 +644,6 @@ public class J3MLoader implements AssetLoader {
case SinglePass:
technique.setLogic(new SinglePassLightingLogic(technique));
break;
case StaticPass:
technique.setLogic(new StaticPassLightingLogic(technique));
break;
case SinglePassAndImageBased:
technique.setLogic(new SinglePassAndImageBasedLightingLogic(technique));
break;

@ -430,7 +430,6 @@ public class MaterialMatParamTest {
private final Geometry geometry = new Geometry("Geometry", new Box(1, 1, 1));
private final Node root = new Node("Root Node");
private final LightList lightList = new LightList(geometry);
@Before
public void setUp() {
@ -526,7 +525,7 @@ public class MaterialMatParamTest {
private void evaluateTechniqueDef() {
Assert.assertFalse(evaluated);
Material mat = geometry.getMaterial();
mat.render(geometry, lightList, renderManager);
mat.render(geometry, renderManager);
Assert.assertTrue(evaluated);
}

@ -0,0 +1,206 @@
/*
* Copyright (c) 2009-2018 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package jme3test.light;
import com.jme3.app.SimpleApplication;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.light.PointLight;
import com.jme3.light.SpotLight;
import com.jme3.material.Material;
import com.jme3.material.TechniqueDef.LightMode;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.post.filters.ToneMapFilter;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Quad;
import com.jme3.shadow.next.InPassShadowRenderer;
import com.jme3.system.AppSettings;
public class TestInPassShadows extends SimpleApplication {
private DirectionalLight dl;
private SpotLight sl;
private PointLight pl;
private InPassShadowRenderer ipsr;
private ToneMapFilter tmf;
public static void main(String[] args) {
TestInPassShadows app = new TestInPassShadows();
AppSettings settings = new AppSettings(true);
settings.setGammaCorrection(true);
app.setSettings(settings);
app.start();
}
@Override
public void simpleInitApp() {
renderManager.setPreferredLightMode(LightMode.SinglePassAndImageBased);
renderManager.setSinglePassLightBatchSize(3);
cam.setLocation(new Vector3f(8.079489f, 10.792628f, -6.714233f));
cam.setRotation(new Quaternion(0.38442945f, -0.35025623f, 0.16050051f, 0.8389125f));
flyCam.setMoveSpeed(5);
tmf = new ToneMapFilter(new Vector3f(50, 50, 50));
FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
fpp.addFilter(tmf);
viewPort.addProcessor(fpp);
loadLights();
loadScene();
loadInputs();
}
private void loadLights() {
AmbientLight al = new AmbientLight(new ColorRGBA(0.2f, 0.2f, 0.3f, 1.0f).mult(2f));
rootNode.addLight(al);
dl = new DirectionalLight();
dl.setDirection(new Vector3f(-1, -0.5f, -1).normalizeLocal());
dl.setColor(new ColorRGBA(1, 0.9f, 0.8f, 1).mult(2.5f));
rootNode.addLight(dl);
sl = new SpotLight();
sl.setSpotRange(15);
sl.setSpotInnerAngle(20 * FastMath.DEG_TO_RAD);
sl.setSpotOuterAngle(25 * FastMath.DEG_TO_RAD);
sl.setPosition(new Vector3f(-5.2193f, -0.5851393f, 4.831882f));
sl.setDirection(new Vector3f(0.8429418f, -0.42458484f, -0.33041906f));
sl.setColor(new ColorRGBA(0.5f, 0.7f, 1.0f, 1.0f).mult(50));
rootNode.addLight(sl);
pl = new PointLight(
new Vector3f(-0.10135013f, 1.9986207f, -2.0745828f),
new ColorRGBA(0.5f, 0.3f, 0.1f, 1f).mult(20),
30);
rootNode.addLight(pl);
ipsr = new InPassShadowRenderer();
ipsr.setTextureSize(512);
ipsr.setPolyOffset(5, 0);
ipsr.directional().setNumSplits(1);
ipsr.addLight(dl);
ipsr.addLight(sl);
ipsr.addLight(pl);
viewPort.addProcessor(ipsr);
}
private void loadScene() {
Geometry box = new Geometry("Box", new Box(1, 1, 1));
box.setShadowMode(ShadowMode.CastAndReceive);
Material boxMat = new Material(assetManager, "Common/MatDefs/Light/PBRLighting.j3md");
boxMat.setFloat("Roughness", 0.5f);
boxMat.setFloat("Metallic", 0f);
box.setMaterial(boxMat);
rootNode.attachChild(box);
Geometry box2 = box.clone(true);
box2.move(3, 0, 0);
rootNode.attachChild(box2);
Geometry box3 = box.clone(true);
box3.move(-3, 0, 0);
rootNode.attachChild(box3);
Geometry floor = new Geometry("floor", new Quad(100, 100));
floor.rotate(-FastMath.HALF_PI, 0, 0);
floor.center();
floor.move(0, -1, 0);
floor.setShadowMode(ShadowMode.Receive);
Material floorMat = new Material(assetManager, "Common/MatDefs/Light/PBRLighting.j3md");
floorMat.setFloat("Roughness", 0.5f);
floorMat.setFloat("Metallic", 0f);
floor.setMaterial(floorMat);
rootNode.attachChild(floor);
}
private boolean moveLight = false;
private void loadInputs() {
inputManager.addMapping("MoveLight", new KeyTrigger(KeyInput.KEY_SPACE));
inputManager.addListener(new ActionListener() {
@Override
public void onAction(String name, boolean isPressed, float tpf) {
moveLight = isPressed;
}
}, "MoveLight");
inputManager.addMapping("OffsetFactorUp", new KeyTrigger(KeyInput.KEY_U));
inputManager.addMapping("OffsetFactorDown", new KeyTrigger(KeyInput.KEY_J));
inputManager.addMapping("OffsetUnitsUp", new KeyTrigger(KeyInput.KEY_I));
inputManager.addMapping("OffsetUnitsDown", new KeyTrigger(KeyInput.KEY_K));
inputManager.addListener(new AnalogListener() {
private float factor, units;
@Override
public void onAnalog(String name, float value, float tpf) {
switch (name) {
case "OffsetFactorUp":
factor += tpf * 5f;
break;
case "OffsetFactorDown":
factor -= tpf * 5f;
break;
case "OffsetUnitsUp":
units += tpf * 50f;
break;
case "OffsetUnitsDown":
units -= tpf * 50f;
break;
}
ipsr.setPolyOffset(factor, units);
System.out.println("PolyOffset(" + factor + ", " + units + ")");
}
}, "OffsetFactorUp", "OffsetFactorDown", "OffsetUnitsUp", "OffsetUnitsDown");
}
@Override
public void simpleUpdate(float tpf) {
if (moveLight) {
sl.setPosition(cam.getLocation());
sl.setDirection(cam.getDirection());
System.out.println(sl.getPosition());
System.out.println(sl.getDirection());
}
}
}

@ -453,7 +453,7 @@ public class JmeBatchRenderBackend implements BatchRenderBackend {
private final Mesh mesh = new Mesh();
private final Geometry meshGeometry = new Geometry("nifty-quad", mesh);
private final RenderState renderState = new RenderState();
private final RenderState renderState = RenderState.ADDITIONAL.clone();
private FloatBuffer vertexPosBuffer;
private FloatBuffer vertexTexCoordBuffer;

@ -73,7 +73,7 @@ public class RenderDeviceJme implements RenderDevice {
private VertexBuffer quadColor;
private Matrix4f tempMat = new Matrix4f();
private ColorRGBA tempColor = new ColorRGBA();
private RenderState renderState = new RenderState();
private RenderState renderState = RenderState.ADDITIONAL.clone();
private Material colorMaterial;
private Material textureColorMaterial;

@ -145,7 +145,7 @@ public class DirectionalLightShadowRendererVR extends AbstractShadowRendererVR {
//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);
ShadowUtil.updateFrustumPoints(viewCam, frustumNear, zFar, points);
//shadowCam.setDirection(direction);
shadowCam.getRotation().lookAt(light.getDirection(), shadowCam.getUp());
@ -180,7 +180,7 @@ public class DirectionalLightShadowRendererVR extends AbstractShadowRendererVR {
protected GeometryList getOccludersToRender(int shadowMapIndex, GeometryList shadowMapOccluders) {
// update frustum points based on current camera and split
ShadowUtil.updateFrustumPoints(viewPort.getCamera(), splitsArray[shadowMapIndex], splitsArray[shadowMapIndex + 1], 1.0f, points);
ShadowUtil.updateFrustumPoints(viewPort.getCamera(), splitsArray[shadowMapIndex], splitsArray[shadowMapIndex + 1], points);
//Updating shadow cam with current split frusta
if (lightReceivers.size()==0) {

Loading…
Cancel
Save