diff --git a/jme3-core/src/main/java/com/jme3/light/DefaultLightFilter.java b/jme3-core/src/main/java/com/jme3/light/DefaultLightFilter.java index df735095ee..a9023f5516 100644 --- a/jme3-core/src/main/java/com/jme3/light/DefaultLightFilter.java +++ b/jme3-core/src/main/java/com/jme3/light/DefaultLightFilter.java @@ -59,6 +59,7 @@ public void setCamera(Camera camera) { for (Light light : processedLights) { light.frustumCheckNeeded = true; } + processedLights.clear(); } @Override diff --git a/jme3-core/src/main/java/com/jme3/light/Light.java b/jme3-core/src/main/java/com/jme3/light/Light.java index 0a66fb729a..af3a540746 100644 --- a/jme3-core/src/main/java/com/jme3/light/Light.java +++ b/jme3-core/src/main/java/com/jme3/light/Light.java @@ -37,6 +37,7 @@ 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 int getId(){ boolean frustumCheckNeeded = true; boolean intersectsFrustum = false; + protected ShadowMap shadowMap; + protected Light() { } @@ -163,6 +166,22 @@ public float getLastDistance(){ 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. diff --git a/jme3-core/src/main/java/com/jme3/light/LightList.java b/jme3-core/src/main/java/com/jme3/light/LightList.java index dfeb654057..5ae42ea589 100644 --- a/jme3-core/src/main/java/com/jme3/light/LightList.java +++ b/jme3-core/src/main/java/com/jme3/light/LightList.java @@ -186,7 +186,7 @@ public void clear() { 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 void sort(boolean transformChanged) { } } + public void sort(Comparator 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. diff --git a/jme3-core/src/main/java/com/jme3/material/Material.java b/jme3-core/src/main/java/com/jme3/material/Material.java index ddd75ace7a..4d9a9cf393 100644 --- a/jme3-core/src/main/java/com/jme3/material/Material.java +++ b/jme3-core/src/main/java/com/jme3/material/Material.java @@ -78,9 +78,9 @@ public class Material implements CloneableSmartAsset, Cloneable, Savable { private AssetKey key; private String name; private MaterialDef def; - private ListMap paramValues = new ListMap(); + private ListMap paramValues = new ListMap<>(); private Technique technique; - private HashMap techniques = new HashMap(); + private HashMap techniques = new HashMap<>(); private RenderState additionalState = null; private RenderState mergedRenderState = new RenderState(); private boolean transparent = false; @@ -140,10 +140,12 @@ public void setName(String name) { this.name = name; } + @Override public void setKey(AssetKey key) { this.key = key; } + @Override public AssetKey getKey() { return key; } @@ -197,9 +199,9 @@ public Material clone() { mat.additionalState = additionalState.clone(); } mat.technique = null; - mat.techniques = new HashMap(); + mat.techniques = new HashMap<>(); - mat.paramValues = new ListMap(); + mat.paramValues = new ListMap<>(); for (int i = 0; i < paramValues.size(); i++) { Map.Entry entry = paramValues.getEntry(i); mat.paramValues.put(entry.getKey(), entry.getValue().clone()); @@ -866,7 +868,14 @@ private boolean isBO(final VarType type) { 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 void preload(RenderManager renderManager, Geometry geometry) { // Get world overrides SafeArrayList 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 @@ private void resetUniformsNotSetByCurrent(Shader shader) { * * * @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 void render(Geometry geometry, LightList lights, RenderManager renderMana SafeArrayList 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 void render(Geometry geometry, LightList lights, RenderManager renderMana 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 void write(JmeExporter ex) throws IOException { } @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 void read(JmeImporter im) throws IOException { } def = (MaterialDef) im.getAssetManager().loadAsset(new AssetKey(defName)); - paramValues = new ListMap(); + paramValues = new ListMap<>(); // load the textures and update nextTexUnit for (Map.Entry entry : params.entrySet()) { diff --git a/jme3-core/src/main/java/com/jme3/material/RenderState.java b/jme3-core/src/main/java/com/jme3/material/RenderState.java index d2a557c65c..fc5819a21a 100644 --- a/jme3-core/src/main/java/com/jme3/material/RenderState.java +++ b/jme3-core/src/main/java/com/jme3/material/RenderState.java @@ -435,6 +435,9 @@ public enum StencilOperation { ADDITIONAL.applyColorWrite = false; ADDITIONAL.applyBlendMode = false; ADDITIONAL.applyPolyOffset = false; + ADDITIONAL.applyStencilTest = false; + ADDITIONAL.applyLineWidth = false; + ADDITIONAL.applyDepthFunc = false; } boolean wireframe = false; boolean applyWireFrame = true; diff --git a/jme3-core/src/main/java/com/jme3/material/Technique.java b/jme3-core/src/main/java/com/jme3/material/Technique.java index 3ae32814ee..e482241ab7 100644 --- a/jme3-core/src/main/java/com/jme3/material/Technique.java +++ b/jme3-core/src/main/java/com/jme3/material/Technique.java @@ -133,9 +133,10 @@ private void applyOverrides(DefineList defineList, SafeArrayList worldOverrides, + Shader makeCurrent(RenderManager renderManager, Geometry geometry, + SafeArrayList worldOverrides, SafeArrayList forcedOverrides, - LightList lights, EnumSet rendererCaps) { + EnumSet rendererCaps) { TechniqueDefLogic logic = def.getLogic(); AssetManager assetManager = owner.getMaterialDef().getAssetManager(); @@ -149,7 +150,7 @@ Shader makeCurrent(RenderManager renderManager, SafeArrayList applyOverrides(dynamicDefines, forcedOverrides); } - return logic.makeCurrent(assetManager, renderManager, rendererCaps, lights, dynamicDefines); + return logic.makeCurrent(assetManager, renderManager, rendererCaps, geometry, dynamicDefines); } /** @@ -161,9 +162,9 @@ Shader makeCurrent(RenderManager renderManager, SafeArrayList * @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); } /** diff --git a/jme3-core/src/main/java/com/jme3/material/TechniqueDef.java b/jme3-core/src/main/java/com/jme3/material/TechniqueDef.java index 1b01115220..d85612fa87 100644 --- a/jme3-core/src/main/java/com/jme3/material/TechniqueDef.java +++ b/jme3-core/src/main/java/com/jme3/material/TechniqueDef.java @@ -105,17 +105,6 @@ public enum LightMode { */ @Deprecated FixedPipeline, - /** - * Similar to {@link #SinglePass} except the type of each light is known - * at shader compile time. - *

- * 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 { diff --git a/jme3-core/src/main/java/com/jme3/material/logic/DefaultTechniqueDefLogic.java b/jme3-core/src/main/java/com/jme3/material/logic/DefaultTechniqueDefLogic.java index 86ce66391f..873029d993 100644 --- a/jme3-core/src/main/java/com/jme3/material/logic/DefaultTechniqueDefLogic.java +++ b/jme3-core/src/main/java/com/jme3/material/logic/DefaultTechniqueDefLogic.java @@ -43,11 +43,13 @@ 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 DefaultTechniqueDefLogic(TechniqueDef techniqueDef) { @Override public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager, - EnumSet rendererCaps, LightList lights, DefineList defines) { + EnumSet rendererCaps, Geometry geometry, DefineList defines) { return techniqueDef.getShader(assetManager, rendererCaps, defines); } @@ -70,6 +72,41 @@ public static void renderMeshFromGeometry(Renderer renderer, Geometry geom) { 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 @@ protected static ColorRGBA getAmbientColor(LightList lightList, boolean removeLi 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); - } } diff --git a/jme3-core/src/main/java/com/jme3/material/logic/MultiPassLightingLogic.java b/jme3-core/src/main/java/com/jme3/material/logic/MultiPassLightingLogic.java index d239681bc3..6d4e7a8122 100644 --- a/jme3-core/src/main/java/com/jme3/material/logic/MultiPassLightingLogic.java +++ b/jme3-core/src/main/java/com/jme3/material/logic/MultiPassLightingLogic.java @@ -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.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 MultiPassLightingLogic(TechniqueDef techniqueDef) { } @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 void render(RenderManager renderManager, Shader shader, Geometry geometry boolean isFirstLight = true; boolean isSecondLight = false; + LightList lights = getFilteredLightList(renderManager, geometry); getAmbientColor(lights, false, ambientLightColor); for (int i = 0; i < lights.size(); i++) { diff --git a/jme3-core/src/main/java/com/jme3/material/logic/SinglePassAndImageBasedLightingLogic.java b/jme3-core/src/main/java/com/jme3/material/logic/SinglePassAndImageBasedLightingLogic.java index f80c8274ae..5ce4dca154 100644 --- a/jme3-core/src/main/java/com/jme3/material/logic/SinglePassAndImageBasedLightingLogic.java +++ b/jme3-core/src/main/java/com/jme3/material/logic/SinglePassAndImageBasedLightingLogic.java @@ -34,13 +34,19 @@ 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 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 rendererCaps, LightList lights, DefineList defines) { - defines.set(nbLightsDefineId, renderManager.getSinglePassLightBatchSize() * 3); + EnumSet 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() { + @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 @@ protected int updateLightListUniforms(Shader shader, Geometry g, LightList light 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 @@ protected int updateLightListUniforms(Shader shader, Geometry g, LightList light 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 @@ private int setProbeData(RenderManager rm, int lastTexUnit, Uniform lightProbeDa } @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; } } diff --git a/jme3-core/src/main/java/com/jme3/material/logic/SinglePassLightingLogic.java b/jme3-core/src/main/java/com/jme3/material/logic/SinglePassLightingLogic.java index 67261742ec..495013b5ac 100644 --- a/jme3-core/src/main/java/com/jme3/material/logic/SinglePassLightingLogic.java +++ b/jme3-core/src/main/java/com/jme3/material/logic/SinglePassLightingLogic.java @@ -77,25 +77,31 @@ public SinglePassLightingLogic(TechniqueDef techniqueDef) { } @Override - public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager, - EnumSet rendererCaps, LightList lights, DefineList defines) { + public Shader makeCurrent( + AssetManager assetManager, + RenderManager renderManager, + EnumSet 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.

* + * Uploads the lights in the light list as two uniform arrays.

+ * *

- * uniform vec4 g_LightColor[numLights];
// - * g_LightColor.rgb is the diffuse/specular color of the light.
// - * g_Lightcolor.a is the type of light, 0 = Directional, 1 = Point,
// - * 2 = Spot.

- * uniform vec4 g_LightPosition[numLights];
// - * g_LightPosition.xyz is the position of the light (for point lights)
- * // or the direction of the light (for directional lights).
// + * uniform vec4 g_LightColor[numLights];
+ * g_LightColor.rgb is the diffuse/specular color of the light.
+ * g_Lightcolor.a is the type of light, 0 = Directional, 1 = Point,
+ * 2 = Spot.

+ * uniform vec4 g_LightPosition[numLights];
+ * g_LightPosition.xyz is the position of the light (for point lights)
+ * // or the direction of the light (for directional lights).
* g_LightPosition.w is the inverse radius (1/r) of the light (for - * attenuation)

+ * attenuation)
+ *

*/ 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 @@ protected int updateLightListUniforms(Shader shader, Geometry g, LightList light lightData.setVector4InArray(color.getRed(), color.getGreen(), color.getBlue(), - l.getType().getId(), + encodeLightType(l), lightDataIndex); lightDataIndex++; @@ -198,10 +204,11 @@ protected int updateLightListUniforms(Shader shader, Geometry g, LightList light } @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); diff --git a/jme3-core/src/main/java/com/jme3/material/logic/StaticPassLightingLogic.java b/jme3-core/src/main/java/com/jme3/material/logic/StaticPassLightingLogic.java deleted file mode 100644 index 48995ba4f5..0000000000 --- a/jme3-core/src/main/java/com/jme3/material/logic/StaticPassLightingLogic.java +++ /dev/null @@ -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 tempDirLights = new ArrayList(); - private final ArrayList tempPointLights = new ArrayList(); - private final ArrayList tempSpotLights = new ArrayList(); - - 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 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); - } - -} diff --git a/jme3-core/src/main/java/com/jme3/material/logic/TechniqueDefLogic.java b/jme3-core/src/main/java/com/jme3/material/logic/TechniqueDefLogic.java index 18a8051861..38d5f4dbdc 100644 --- a/jme3-core/src/main/java/com/jme3/material/logic/TechniqueDefLogic.java +++ b/jme3-core/src/main/java/com/jme3/material/logic/TechniqueDefLogic.java @@ -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 rendererCaps, LightList lights, DefineList defines); + public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager, + EnumSet rendererCaps, Geometry geometry, DefineList defines); /** * Requests that the TechniqueDefLogic renders the given geometry. @@ -88,10 +89,10 @@ public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager * 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); } diff --git a/jme3-core/src/main/java/com/jme3/math/Matrix4f.java b/jme3-core/src/main/java/com/jme3/math/Matrix4f.java index 6b260ee31a..53bdb56bf2 100644 --- a/jme3-core/src/main/java/com/jme3/math/Matrix4f.java +++ b/jme3-core/src/main/java/com/jme3/math/Matrix4f.java @@ -753,7 +753,6 @@ public FloatBuffer fillFloatBuffer(FloatBuffer fb, boolean columnMajor) { TempVars vars = TempVars.get(); - fillFloatArray(vars.matrixWrite, columnMajor); fb.put(vars.matrixWrite, 0, 16); diff --git a/jme3-core/src/main/java/com/jme3/post/PreDepthProcessor.java b/jme3-core/src/main/java/com/jme3/post/PreDepthProcessor.java index a3125dd1e1..92828b8077 100644 --- a/jme3-core/src/main/java/com/jme3/post/PreDepthProcessor.java +++ b/jme3-core/src/main/java/com/jme3/post/PreDepthProcessor.java @@ -60,7 +60,7 @@ public PreDepthProcessor(AssetManager assetManager){ preDepth.getAdditionalRenderState().setPolyOffset(0, 0); preDepth.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Back); - forcedRS = new RenderState(); + forcedRS = RenderState.ADDITIONAL.clone(); forcedRS.setDepthTest(true); forcedRS.setDepthWrite(false); } diff --git a/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java b/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java index cc86b5ad48..fe67ee2a88 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java +++ b/jme3-core/src/main/java/com/jme3/renderer/RenderManager.java @@ -84,7 +84,6 @@ public class RenderManager { private final SafeArrayList 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 void renderGeometry(Geometry geom) { } 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 void renderGeometry(Geometry geom) { : 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); } } diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugES.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugES.java index 8746d57edc..27709bfec3 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugES.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugES.java @@ -221,7 +221,7 @@ public void glGenTextures(IntBuffer textures) { @Override public void glGenQueries(int num, IntBuffer ids) { - glGenQueries(num, ids); + gl.glGenQueries(num, ids); checkError(); } diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLTracer.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLTracer.java index ae3f17a8ca..33b0bef51b 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLTracer.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLTracer.java @@ -90,6 +90,7 @@ private static void noEnumArgs(String method, int... argSlots) { 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 @@ private static void noEnumArgs(String method, int... argSlots) { 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); diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/TextureUtil.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/TextureUtil.java index 114ac09d15..7d2ff78d3c 100644 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/TextureUtil.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/TextureUtil.java @@ -199,10 +199,12 @@ private void uploadTextureLevel(GLImageFormat format, int target, int level, int 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 diff --git a/jme3-core/src/main/java/com/jme3/shader/Uniform.java b/jme3-core/src/main/java/com/jme3/shader/Uniform.java index 0340c8a132..9141048453 100644 --- a/jme3-core/src/main/java/com/jme3/shader/Uniform.java +++ b/jme3-core/src/main/java/com/jme3/shader/Uniform.java @@ -197,7 +197,7 @@ public void clearValue(){ } } - 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 void setValue(VarType type, Object value){ 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 void setVector4Length(int length){ 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 void setVector4InArray(float x, float y, float z, float w, int index){ updateNeeded = true; setByCurrentMaterial = true; } - + public boolean isUpdateNeeded(){ return updateNeeded; } diff --git a/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java index 602b7c89d9..baef0a90a4 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java +++ b/jme3-core/src/main/java/com/jme3/shadow/AbstractShadowRenderer.java @@ -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; diff --git a/jme3-core/src/main/java/com/jme3/shadow/BasicShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/BasicShadowRenderer.java index 4beb4000c9..e184b7fa24 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/BasicShadowRenderer.java +++ b/jme3-core/src/main/java/com/jme3/shadow/BasicShadowRenderer.java @@ -158,7 +158,6 @@ public void postQueue(RenderQueue rq) { ShadowUtil.updateFrustumPoints(viewCam, viewCam.getFrustumNear(), viewCam.getFrustumFar(), - 1.0f, points); Vector3f frustaCenter = new Vector3f(); diff --git a/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java index e203f16a90..f3d5c952ac 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java +++ b/jme3-core/src/main/java/com/jme3/shadow/DirectionalLightShadowRenderer.java @@ -148,7 +148,7 @@ protected void updateShadowCams(Camera viewCam) { //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 @@ protected void updateShadowCams(Camera viewCam) { 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) { diff --git a/jme3-core/src/main/java/com/jme3/shadow/PssmShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/PssmShadowRenderer.java index 2361be28a0..08164d1b6a 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/PssmShadowRenderer.java +++ b/jme3-core/src/main/java/com/jme3/shadow/PssmShadowRenderer.java @@ -398,7 +398,7 @@ public void postQueue(RenderQueue rq) { //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 void postQueue(RenderQueue rq) { 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); diff --git a/jme3-core/src/main/java/com/jme3/shadow/PssmShadowUtil.java b/jme3-core/src/main/java/com/jme3/shadow/PssmShadowUtil.java index 888dc2fa81..64b36379b9 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/PssmShadowUtil.java +++ b/jme3-core/src/main/java/com/jme3/shadow/PssmShadowUtil.java @@ -67,15 +67,4 @@ public static void updateFrustumSplits(float[] splits, float near, float far, fl 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()); - } } diff --git a/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java b/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java index 653d044881..a7ea7b380a 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java +++ b/jme3-core/src/main/java/com/jme3/shadow/ShadowUtil.java @@ -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.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 static void updateFrustumPoints2(Camera viewCam, Vector3f[] points) { public static void updateFrustumPoints(Camera viewCam, float nearOverride, float farOverride, - float scale, Vector3f[] points) { Vector3f pos = viewCam.getLocation(); @@ -149,22 +151,6 @@ public static void updateFrustumPoints(Camera viewCam, 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 static BoundingBox computeBoundForPoints(Vector3f[] pts, Matrix4f mat) { 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 static class OccludersExtractor { // 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 static class OccludersExtractor 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 OccludersExtractor(Matrix4f vpm, int cc, BoundingBox sBB, BoundingBox cBB * 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 static void updateShadowCamera(ViewPort viewPort, GeometryList receivers, Camera shadowCam, Vector3f[] points, - GeometryList splitOccluders, + GeometryList shadowCasters, float shadowMapSize) { boolean ortho = shadowCam.isParallelProjection(); @@ -465,7 +388,7 @@ public static void updateShadowCamera(ViewPort viewPort, 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 static void updateShadowCamera(ViewPort viewPort, 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 static void updateShadowCamera(ViewPort viewPort, 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; diff --git a/jme3-core/src/main/java/com/jme3/shadow/SpotLightShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/SpotLightShadowRenderer.java index 32eef5630d..3f04f3d1a1 100644 --- a/jme3-core/src/main/java/com/jme3/shadow/SpotLightShadowRenderer.java +++ b/jme3-core/src/main/java/com/jme3/shadow/SpotLightShadowRenderer.java @@ -137,7 +137,7 @@ protected void updateShadowCams(Camera viewCam) { //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()); diff --git a/jme3-core/src/main/java/com/jme3/shadow/next/InPassShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/next/InPassShadowRenderer.java new file mode 100644 index 0000000000..c36a004da5 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/shadow/next/InPassShadowRenderer.java @@ -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. + *

+ * 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 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(), + 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) { + } + +} diff --git a/jme3-core/src/main/java/com/jme3/shadow/next/PreShadowRenderer.java b/jme3-core/src/main/java/com/jme3/shadow/next/PreShadowRenderer.java new file mode 100755 index 0000000000..ef05b1dcdb --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/shadow/next/PreShadowRenderer.java @@ -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. + *

+ * 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 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) { + } + +} diff --git a/jme3-core/src/main/java/com/jme3/shadow/next/ShadowDebugControl.java b/jme3-core/src/main/java/com/jme3/shadow/next/ShadowDebugControl.java new file mode 100644 index 0000000000..2607525e7c --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/shadow/next/ShadowDebugControl.java @@ -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 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) { + } + +} diff --git a/jme3-core/src/main/java/com/jme3/shadow/next/ShadowMap.java b/jme3-core/src/main/java/com/jme3/shadow/next/ShadowMap.java new file mode 100755 index 0000000000..f0a7807ccf --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/shadow/next/ShadowMap.java @@ -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 Type of light + * @author Kirill Vainer + */ +public interface ShadowMap { + + public Type getLightType(); + + public int getNumSlices(); + + public ShadowMapSlice getSlice(int index); + +} diff --git a/jme3-core/src/main/java/com/jme3/shadow/next/ShadowMapSlice.java b/jme3-core/src/main/java/com/jme3/shadow/next/ShadowMapSlice.java new file mode 100755 index 0000000000..8f5bb210cc --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/shadow/next/ShadowMapSlice.java @@ -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 Type of light + * + * @author Kirill Vainer + */ +public interface ShadowMapSlice { + + 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); +} diff --git a/jme3-core/src/main/java/com/jme3/shadow/next/array/ArrayShadowMap.java b/jme3-core/src/main/java/com/jme3/shadow/next/array/ArrayShadowMap.java new file mode 100755 index 0000000000..fe5fdcc834 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/shadow/next/array/ArrayShadowMap.java @@ -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(); +} diff --git a/jme3-core/src/main/java/com/jme3/shadow/next/array/ArrayShadowMapSlice.java b/jme3-core/src/main/java/com/jme3/shadow/next/array/ArrayShadowMapSlice.java new file mode 100755 index 0000000000..6d3897db28 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/shadow/next/array/ArrayShadowMapSlice.java @@ -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 extends ShadowMapSlice { +} diff --git a/jme3-core/src/main/java/com/jme3/shadow/next/array/BaseArrayShadowMap.java b/jme3-core/src/main/java/com/jme3/shadow/next/array/BaseArrayShadowMap.java new file mode 100755 index 0000000000..bb7e8024c5 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/shadow/next/array/BaseArrayShadowMap.java @@ -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 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]; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/shadow/next/array/BaseArrayShadowMapSlice.java b/jme3-core/src/main/java/com/jme3/shadow/next/array/BaseArrayShadowMapSlice.java new file mode 100755 index 0000000000..5cbd37396a --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/shadow/next/array/BaseArrayShadowMapSlice.java @@ -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 + * @author Kirill Vainer + */ +public class BaseArrayShadowMapSlice implements ArrayShadowMapSlice { + + 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); + } +} diff --git a/jme3-core/src/main/java/com/jme3/shadow/next/array/DirectionalArrayShadowMap.java b/jme3-core/src/main/java/com/jme3/shadow/next/array/DirectionalArrayShadowMap.java new file mode 100755 index 0000000000..f9cf303ac5 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/shadow/next/array/DirectionalArrayShadowMap.java @@ -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 { + + 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; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/shadow/next/array/DirectionalArrayShadowMapSlice.java b/jme3-core/src/main/java/com/jme3/shadow/next/array/DirectionalArrayShadowMapSlice.java new file mode 100755 index 0000000000..edb9ae810d --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/shadow/next/array/DirectionalArrayShadowMapSlice.java @@ -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 { + + 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); + } + +} diff --git a/jme3-core/src/main/java/com/jme3/shadow/next/array/PointArrayShadowMap.java b/jme3-core/src/main/java/com/jme3/shadow/next/array/PointArrayShadowMap.java new file mode 100644 index 0000000000..67e2bd35d2 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/shadow/next/array/PointArrayShadowMap.java @@ -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 { + + 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; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/shadow/next/array/PointArrayShadowMapSlice.java b/jme3-core/src/main/java/com/jme3/shadow/next/array/PointArrayShadowMapSlice.java new file mode 100644 index 0000000000..91d46fcd3d --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/shadow/next/array/PointArrayShadowMapSlice.java @@ -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 { + + 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); + } + } +} diff --git a/jme3-core/src/main/java/com/jme3/shadow/next/array/SpotArrayShadowMap.java b/jme3-core/src/main/java/com/jme3/shadow/next/array/SpotArrayShadowMap.java new file mode 100755 index 0000000000..f6c72a762d --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/shadow/next/array/SpotArrayShadowMap.java @@ -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 { + + 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; + } +} diff --git a/jme3-core/src/main/java/com/jme3/shadow/next/array/SpotArrayShadowMapSlice.java b/jme3-core/src/main/java/com/jme3/shadow/next/array/SpotArrayShadowMapSlice.java new file mode 100755 index 0000000000..eb93a2c3ab --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/shadow/next/array/SpotArrayShadowMapSlice.java @@ -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 { + + 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); + } + } +} diff --git a/jme3-core/src/main/java/com/jme3/shadow/next/pssm/BaseShadowMapSlice.java b/jme3-core/src/main/java/com/jme3/shadow/next/pssm/BaseShadowMapSlice.java new file mode 100755 index 0000000000..9a46890e16 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/shadow/next/pssm/BaseShadowMapSlice.java @@ -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 implements ShadowMapSlice { + + 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); + } +} diff --git a/jme3-core/src/main/java/com/jme3/shadow/next/pssm/DirectionalShadowMap.java b/jme3-core/src/main/java/com/jme3/shadow/next/pssm/DirectionalShadowMap.java new file mode 100755 index 0000000000..bc30b9affb --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/shadow/next/pssm/DirectionalShadowMap.java @@ -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 { + + 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; + } + +} diff --git a/jme3-core/src/main/java/com/jme3/shadow/next/pssm/DirectionalShadowMapSlice.java b/jme3-core/src/main/java/com/jme3/shadow/next/pssm/DirectionalShadowMapSlice.java new file mode 100755 index 0000000000..494dec1c24 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/shadow/next/pssm/DirectionalShadowMapSlice.java @@ -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 { + + 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; + } +} diff --git a/jme3-core/src/main/java/com/jme3/shadow/next/pssm/DirectionalShadowParameters.java b/jme3-core/src/main/java/com/jme3/shadow/next/pssm/DirectionalShadowParameters.java new file mode 100755 index 0000000000..0738cce0bd --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/shadow/next/pssm/DirectionalShadowParameters.java @@ -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; + } + } +} diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.frag b/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.frag index 9b8aa27097..17db8f7b10 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.frag +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.frag @@ -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; diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.j3md b/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.j3md index 2a8847324e..980ac650bf 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.j3md +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.j3md @@ -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 } diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.vert b/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.vert index 6c74a75578..30342c5246 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.vert +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/Lighting.vert @@ -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 diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag index 716d3ff7ab..45364c3bbc 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.frag @@ -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) diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.j3md b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.j3md index e300bffdca..adea987780 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.j3md +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.j3md @@ -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 } diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.vert b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.vert index b910a8d4b2..37ead2b7c1 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.vert +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/PBRLighting.vert @@ -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) diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.frag b/jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.frag index a510fc1dbc..472b7c1283 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.frag +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.frag @@ -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 diff --git a/jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.vert b/jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.vert index f9fbe40cfb..1da0c76c4f 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.vert +++ b/jme3-core/src/main/resources/Common/MatDefs/Light/SPLighting.vert @@ -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 diff --git a/jme3-core/src/main/resources/Common/MatDefs/Misc/Particle.j3md b/jme3-core/src/main/resources/Common/MatDefs/Misc/Particle.j3md index 504062ffaa..f51825aef5 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Misc/Particle.j3md +++ b/jme3-core/src/main/resources/Common/MatDefs/Misc/Particle.j3md @@ -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 { diff --git a/jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.j3md b/jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.j3md index f765d20445..fec38f503d 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.j3md +++ b/jme3-core/src/main/resources/Common/MatDefs/Misc/Unshaded.j3md @@ -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 } diff --git a/jme3-core/src/main/resources/Common/MatDefs/Shadow/PreShadow.j3md b/jme3-core/src/main/resources/Common/MatDefs/Shadow/PreShadow.j3md index a76b36310b..629eb165b2 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Shadow/PreShadow.j3md +++ b/jme3-core/src/main/resources/Common/MatDefs/Shadow/PreShadow.j3md @@ -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 { diff --git a/jme3-core/src/main/resources/Common/MatDefs/Shadow/PreShadow.vert b/jme3-core/src/main/resources/Common/MatDefs/Shadow/PreShadow.vert index 7157eea306..78bb2297f1 100644 --- a/jme3-core/src/main/resources/Common/MatDefs/Shadow/PreShadow.vert +++ b/jme3-core/src/main/resources/Common/MatDefs/Shadow/PreShadow.vert @@ -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 } \ No newline at end of file diff --git a/jme3-core/src/main/resources/Common/MatDefs/Shadow/ShowShadowArray.frag b/jme3-core/src/main/resources/Common/MatDefs/Shadow/ShowShadowArray.frag new file mode 100644 index 0000000000..ad44a891db --- /dev/null +++ b/jme3-core/src/main/resources/Common/MatDefs/Shadow/ShowShadowArray.frag @@ -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; +} diff --git a/jme3-core/src/main/resources/Common/MatDefs/Shadow/ShowShadowArray.j3md b/jme3-core/src/main/resources/Common/MatDefs/Shadow/ShowShadowArray.j3md new file mode 100644 index 0000000000..6c89f2043d --- /dev/null +++ b/jme3-core/src/main/resources/Common/MatDefs/Shadow/ShowShadowArray.j3md @@ -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 + } + } +} diff --git a/jme3-core/src/main/resources/Common/ShaderLib/GLSLCompat.glsllib b/jme3-core/src/main/resources/Common/ShaderLib/GLSLCompat.glsllib index c1ace91d12..2a035e2f52 100644 --- a/jme3-core/src/main/resources/Common/ShaderLib/GLSLCompat.glsllib +++ b/jme3-core/src/main/resources/Common/ShaderLib/GLSLCompat.glsllib @@ -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 diff --git a/jme3-core/src/main/resources/Common/ShaderLib/InPassShadows.glsllib b/jme3-core/src/main/resources/Common/ShaderLib/InPassShadows.glsllib new file mode 100755 index 0000000000..3cf681f84f --- /dev/null +++ b/jme3-core/src/main/resources/Common/ShaderLib/InPassShadows.glsllib @@ -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 diff --git a/jme3-core/src/main/resources/Common/ShaderLib/Lighting.glsllib b/jme3-core/src/main/resources/Common/ShaderLib/Lighting.glsllib index fb8f40524f..dadb665714 100644 --- a/jme3-core/src/main/resources/Common/ShaderLib/Lighting.glsllib +++ b/jme3-core/src/main/resources/Common/ShaderLib/Lighting.glsllib @@ -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); diff --git a/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java b/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java index c3ac904a56..80c19dc7da 100644 --- a/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java +++ b/jme3-core/src/plugins/java/com/jme3/material/plugins/J3MLoader.java @@ -39,7 +39,6 @@ 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 @@ private void readTechnique(Statement techStat) throws IOException{ case SinglePass: technique.setLogic(new SinglePassLightingLogic(technique)); break; - case StaticPass: - technique.setLogic(new StaticPassLightingLogic(technique)); - break; case SinglePassAndImageBased: technique.setLogic(new SinglePassAndImageBasedLightingLogic(technique)); break; diff --git a/jme3-core/src/test/java/com/jme3/material/MaterialMatParamTest.java b/jme3-core/src/test/java/com/jme3/material/MaterialMatParamTest.java index fc4503b1aa..a8795fba97 100644 --- a/jme3-core/src/test/java/com/jme3/material/MaterialMatParamTest.java +++ b/jme3-core/src/test/java/com/jme3/material/MaterialMatParamTest.java @@ -430,7 +430,6 @@ public String toString() { 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 @@ private void material(String path) { private void evaluateTechniqueDef() { Assert.assertFalse(evaluated); Material mat = geometry.getMaterial(); - mat.render(geometry, lightList, renderManager); + mat.render(geometry, renderManager); Assert.assertTrue(evaluated); } diff --git a/jme3-examples/src/main/java/jme3test/light/TestInPassShadows.java b/jme3-examples/src/main/java/jme3test/light/TestInPassShadows.java new file mode 100755 index 0000000000..6a1ad74c39 --- /dev/null +++ b/jme3-examples/src/main/java/jme3test/light/TestInPassShadows.java @@ -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()); + } + } + +} diff --git a/jme3-niftygui/src/main/java/com/jme3/niftygui/JmeBatchRenderBackend.java b/jme3-niftygui/src/main/java/com/jme3/niftygui/JmeBatchRenderBackend.java index a8947fdea6..10104224cb 100644 --- a/jme3-niftygui/src/main/java/com/jme3/niftygui/JmeBatchRenderBackend.java +++ b/jme3-niftygui/src/main/java/com/jme3/niftygui/JmeBatchRenderBackend.java @@ -453,7 +453,7 @@ private class Batch { 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; diff --git a/jme3-niftygui/src/main/java/com/jme3/niftygui/RenderDeviceJme.java b/jme3-niftygui/src/main/java/com/jme3/niftygui/RenderDeviceJme.java index ab0fc6659e..071cbbe057 100644 --- a/jme3-niftygui/src/main/java/com/jme3/niftygui/RenderDeviceJme.java +++ b/jme3-niftygui/src/main/java/com/jme3/niftygui/RenderDeviceJme.java @@ -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; diff --git a/jme3-vr/src/main/java/com/jme3/shadow/DirectionalLightShadowRendererVR.java b/jme3-vr/src/main/java/com/jme3/shadow/DirectionalLightShadowRendererVR.java index 4c87a39d79..318407a826 100644 --- a/jme3-vr/src/main/java/com/jme3/shadow/DirectionalLightShadowRendererVR.java +++ b/jme3-vr/src/main/java/com/jme3/shadow/DirectionalLightShadowRendererVR.java @@ -145,7 +145,7 @@ protected void updateShadowCams(Camera viewCam) { //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 @@ protected void updateShadowCams(Camera viewCam) { 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) {