Compare commits
56 Commits
master
...
in-pass-sh
Author | SHA1 | Date |
---|---|---|
Nehon | dd2626c560 | 7 years ago |
Nehon | d444c28183 | 7 years ago |
Nehon | 54ef6ec280 | 7 years ago |
Kirill Vainer | cbf6ffaad8 | 7 years ago |
Kirill Vainer | 251511ee00 | 7 years ago |
Kirill Vainer | 2e9996d498 | 7 years ago |
Kirill Vainer | 5f66eeacb4 | 7 years ago |
Kirill Vainer | 5b800952f0 | 7 years ago |
Kirill Vainer | caad16626e | 7 years ago |
Kirill Vainer | 6fb2d029d2 | 7 years ago |
Kirill Vainer | 5108f52ebf | 7 years ago |
Kirill Vainer | 42432ed4ea | 7 years ago |
Kirill Vainer | 6487def9d3 | 7 years ago |
Kirill Vainer | e4536808ca | 7 years ago |
Kirill Vainer | fe158e7b31 | 7 years ago |
Kirill Vainer | 628fa23059 | 7 years ago |
Kirill Vainer | fca6d4a8b2 | 7 years ago |
Kirill Vainer | 8d125a30ba | 7 years ago |
Kirill Vainer | d50fb09efb | 7 years ago |
Kirill Vainer | 59c85d58c8 | 7 years ago |
Kirill Vainer | 4b4bf24127 | 7 years ago |
Kirill Vainer | 0fae3839d3 | 7 years ago |
Kirill Vainer | c3cfab65c6 | 7 years ago |
Kirill Vainer | c136a4212e | 7 years ago |
Kirill Vainer | ec0fcd24d2 | 7 years ago |
Kirill Vainer | 47b34c6de5 | 7 years ago |
Kirill Vainer | 1e861fd2fa | 7 years ago |
Kirill Vainer | b52d0e3743 | 7 years ago |
Kirill Vainer | 3889cb47b7 | 7 years ago |
Kirill Vainer | 2c385914c6 | 7 years ago |
Kirill Vainer | 0a4a439745 | 7 years ago |
Kirill Vainer | a3145885d9 | 7 years ago |
Kirill Vainer | 5aa2c722fe | 7 years ago |
Kirill Vainer | 77e552f551 | 7 years ago |
Kirill Vainer | 9c4fcac876 | 7 years ago |
Kirill Vainer | d159e1746c | 7 years ago |
Kirill Vainer | 8a747276d7 | 7 years ago |
Kirill Vainer | ce28e35393 | 7 years ago |
Kirill Vainer | 42051b045b | 7 years ago |
Kirill Vainer | cfcec44b9a | 7 years ago |
Kirill Vainer | 55e9fd067a | 7 years ago |
Kirill Vainer | f5ad0274b3 | 7 years ago |
Kirill Vainer | 2ce2995956 | 7 years ago |
Kirill Vainer | 4d60b2df70 | 7 years ago |
Kirill Vainer | 16e472678a | 7 years ago |
Kirill Vainer | bc50b09bf4 | 7 years ago |
Kirill Vainer | 134c3651c8 | 7 years ago |
Kirill Vainer | 7441865307 | 7 years ago |
Kirill Vainer | 88aaa079e3 | 7 years ago |
Kirill Vainer | b0316e419c | 7 years ago |
Kirill Vainer | af3a0c70ce | 7 years ago |
Kirill Vainer | 7a22f8c940 | 7 years ago |
Kirill Vainer | 23700d5140 | 7 years ago |
Kirill Vainer | 69139a1e95 | 7 years ago |
Kirill Vainer | 259694605e | 7 years ago |
Kirill Vainer | 406c3144d8 | 7 years ago |
@ -1,182 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2009-2015 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.material.logic; |
||||
|
||||
import com.jme3.asset.AssetManager; |
||||
import com.jme3.light.DirectionalLight; |
||||
import com.jme3.light.Light; |
||||
import com.jme3.light.LightList; |
||||
import com.jme3.light.PointLight; |
||||
import com.jme3.light.SpotLight; |
||||
import com.jme3.material.TechniqueDef; |
||||
import com.jme3.math.ColorRGBA; |
||||
import com.jme3.math.Matrix4f; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.renderer.Caps; |
||||
import com.jme3.renderer.RenderManager; |
||||
import com.jme3.renderer.Renderer; |
||||
import com.jme3.scene.Geometry; |
||||
import com.jme3.shader.DefineList; |
||||
import com.jme3.shader.Shader; |
||||
import com.jme3.shader.Uniform; |
||||
import com.jme3.shader.VarType; |
||||
import java.util.ArrayList; |
||||
import java.util.EnumSet; |
||||
|
||||
/** |
||||
* Rendering logic for static pass. |
||||
* |
||||
* @author Kirill Vainer |
||||
*/ |
||||
public final class StaticPassLightingLogic extends DefaultTechniqueDefLogic { |
||||
|
||||
private static final String DEFINE_NUM_DIR_LIGHTS = "NUM_DIR_LIGHTS"; |
||||
private static final String DEFINE_NUM_POINT_LIGHTS = "NUM_POINT_LIGHTS"; |
||||
private static final String DEFINE_NUM_SPOT_LIGHTS = "NUM_SPOT_LIGHTS"; |
||||
|
||||
private final int numDirLightsDefineId; |
||||
private final int numPointLightsDefineId; |
||||
private final int numSpotLightsDefineId; |
||||
|
||||
private final ArrayList<DirectionalLight> tempDirLights = new ArrayList<DirectionalLight>(); |
||||
private final ArrayList<PointLight> tempPointLights = new ArrayList<PointLight>(); |
||||
private final ArrayList<SpotLight> tempSpotLights = new ArrayList<SpotLight>(); |
||||
|
||||
private final ColorRGBA ambientLightColor = new ColorRGBA(0, 0, 0, 1); |
||||
private final Vector3f tempPosition = new Vector3f(); |
||||
private final Vector3f tempDirection = new Vector3f(); |
||||
|
||||
public StaticPassLightingLogic(TechniqueDef techniqueDef) { |
||||
super(techniqueDef); |
||||
|
||||
numDirLightsDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_NUM_DIR_LIGHTS, VarType.Int); |
||||
numPointLightsDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_NUM_POINT_LIGHTS, VarType.Int); |
||||
numSpotLightsDefineId = techniqueDef.addShaderUnmappedDefine(DEFINE_NUM_SPOT_LIGHTS, VarType.Int); |
||||
} |
||||
|
||||
@Override |
||||
public Shader makeCurrent(AssetManager assetManager, RenderManager renderManager, |
||||
EnumSet<Caps> rendererCaps, LightList lights, DefineList defines) { |
||||
|
||||
// TODO: if it ever changes that render isn't called
|
||||
// right away with the same geometry after makeCurrent, it would be
|
||||
// a problem.
|
||||
// Do a radix sort.
|
||||
tempDirLights.clear(); |
||||
tempPointLights.clear(); |
||||
tempSpotLights.clear(); |
||||
for (Light light : lights) { |
||||
switch (light.getType()) { |
||||
case Directional: |
||||
tempDirLights.add((DirectionalLight) light); |
||||
break; |
||||
case Point: |
||||
tempPointLights.add((PointLight) light); |
||||
break; |
||||
case Spot: |
||||
tempSpotLights.add((SpotLight) light); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
defines.set(numDirLightsDefineId, tempDirLights.size()); |
||||
defines.set(numPointLightsDefineId, tempPointLights.size()); |
||||
defines.set(numSpotLightsDefineId, tempSpotLights.size()); |
||||
|
||||
return techniqueDef.getShader(assetManager, rendererCaps, defines); |
||||
} |
||||
|
||||
private void transformDirection(Matrix4f viewMatrix, Vector3f direction) { |
||||
viewMatrix.multNormal(direction, direction); |
||||
} |
||||
|
||||
private void transformPosition(Matrix4f viewMatrix, Vector3f location) { |
||||
viewMatrix.mult(location, location); |
||||
} |
||||
|
||||
private void updateLightListUniforms(Matrix4f viewMatrix, Shader shader, LightList lights) { |
||||
Uniform ambientColor = shader.getUniform("g_AmbientLightColor"); |
||||
ambientColor.setValue(VarType.Vector4, getAmbientColor(lights, true, ambientLightColor)); |
||||
|
||||
Uniform lightData = shader.getUniform("g_LightData"); |
||||
|
||||
int totalSize = tempDirLights.size() * 2 |
||||
+ tempPointLights.size() * 2 |
||||
+ tempSpotLights.size() * 3; |
||||
lightData.setVector4Length(totalSize); |
||||
|
||||
int index = 0; |
||||
for (DirectionalLight light : tempDirLights) { |
||||
ColorRGBA color = light.getColor(); |
||||
tempDirection.set(light.getDirection()); |
||||
transformDirection(viewMatrix, tempDirection); |
||||
lightData.setVector4InArray(color.r, color.g, color.b, 1f, index++); |
||||
lightData.setVector4InArray(tempDirection.x, tempDirection.y, tempDirection.z, 1f, index++); |
||||
} |
||||
|
||||
for (PointLight light : tempPointLights) { |
||||
ColorRGBA color = light.getColor(); |
||||
tempPosition.set(light.getPosition()); |
||||
float invRadius = light.getInvRadius(); |
||||
transformPosition(viewMatrix, tempPosition); |
||||
lightData.setVector4InArray(color.r, color.g, color.b, 1f, index++); |
||||
lightData.setVector4InArray(tempPosition.x, tempPosition.y, tempPosition.z, invRadius, index++); |
||||
} |
||||
|
||||
for (SpotLight light : tempSpotLights) { |
||||
ColorRGBA color = light.getColor(); |
||||
Vector3f pos = light.getPosition(); |
||||
Vector3f dir = light.getDirection(); |
||||
|
||||
tempPosition.set(light.getPosition()); |
||||
tempDirection.set(light.getDirection()); |
||||
transformPosition(viewMatrix, tempPosition); |
||||
transformDirection(viewMatrix, tempDirection); |
||||
|
||||
float invRange = light.getInvSpotRange(); |
||||
float spotAngleCos = light.getPackedAngleCos(); |
||||
lightData.setVector4InArray(color.r, color.g, color.b, 1f, index++); |
||||
lightData.setVector4InArray(tempPosition.x, tempPosition.y, tempPosition.z, invRange, index++); |
||||
lightData.setVector4InArray(tempDirection.x, tempDirection.y, tempDirection.z, spotAngleCos, index++); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void render(RenderManager renderManager, Shader shader, Geometry geometry, LightList lights, int lastTexUnit) { |
||||
Renderer renderer = renderManager.getRenderer(); |
||||
Matrix4f viewMatrix = renderManager.getCurrentCamera().getViewMatrix(); |
||||
updateLightListUniforms(viewMatrix, shader, lights); |
||||
renderer.setShader(shader); |
||||
renderMeshFromGeometry(renderer, geometry); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,275 @@ |
||||
/* |
||||
* Copyright (c) 2009-2016 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.shadow.next; |
||||
|
||||
import com.jme3.asset.AssetManager; |
||||
import com.jme3.shadow.next.pssm.DirectionalShadowParameters; |
||||
import com.jme3.light.DirectionalLight; |
||||
import com.jme3.light.Light; |
||||
import com.jme3.light.Light.Type; |
||||
import com.jme3.light.PointLight; |
||||
import com.jme3.light.SpotLight; |
||||
import com.jme3.material.MatParamOverride; |
||||
import com.jme3.material.RenderState; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.post.SceneProcessor; |
||||
import com.jme3.profile.AppProfiler; |
||||
import com.jme3.renderer.RenderManager; |
||||
import com.jme3.renderer.Renderer; |
||||
import com.jme3.renderer.ViewPort; |
||||
import com.jme3.renderer.queue.GeometryList; |
||||
import com.jme3.renderer.queue.OpaqueComparator; |
||||
import com.jme3.renderer.queue.RenderQueue; |
||||
import com.jme3.scene.Node; |
||||
import com.jme3.shader.VarType; |
||||
import com.jme3.shadow.next.array.DirectionalArrayShadowMap; |
||||
import com.jme3.shadow.next.array.PointArrayShadowMap; |
||||
import com.jme3.shadow.next.array.SpotArrayShadowMap; |
||||
import com.jme3.texture.FrameBuffer; |
||||
import com.jme3.texture.Image; |
||||
import com.jme3.texture.Image.Format; |
||||
import com.jme3.texture.Texture.MagFilter; |
||||
import com.jme3.texture.Texture.MinFilter; |
||||
import com.jme3.texture.Texture.ShadowCompareMode; |
||||
import com.jme3.texture.TextureArray; |
||||
import com.jme3.texture.image.ColorSpace; |
||||
import com.jme3.util.ListMap; |
||||
import com.jme3.util.TempVars; |
||||
import java.nio.ByteBuffer; |
||||
import java.util.ArrayList; |
||||
|
||||
/** |
||||
* The 4th generation of shadow mapping in jME3. |
||||
* <p> |
||||
* This version is primarily focused on rendering in-pass shadows, so pre-pass |
||||
* and subsequent stages are separated. |
||||
* |
||||
* @author Kirill Vainer |
||||
*/ |
||||
public class InPassShadowRenderer implements SceneProcessor { |
||||
|
||||
private static final String PRE_SHADOW_TECHNIQUE_NAME = "PreShadow"; |
||||
|
||||
private RenderManager renderManager; |
||||
private ViewPort viewPort; |
||||
private final Vector3f[] points = new Vector3f[8]; |
||||
private final GeometryList shadowCasters = new GeometryList(new OpaqueComparator()); |
||||
private final ListMap<Light, ShadowMap> shadowedLights = new ListMap<>(); |
||||
private final RenderState prePassRenderState = RenderState.ADDITIONAL.clone(); |
||||
private final MatParamOverride pointLightOverride = new MatParamOverride(VarType.Boolean, "IsPointLight", true); |
||||
private final TextureArray array = new TextureArray(); |
||||
|
||||
private int textureSize = 1024; |
||||
private int nextArraySlice = 0; |
||||
|
||||
// parameters for directional lights
|
||||
private final DirectionalShadowParameters directionalParams = new DirectionalShadowParameters(); |
||||
|
||||
public InPassShadowRenderer() { |
||||
for (int i = 0; i < points.length; i++) { |
||||
points[i] = new Vector3f(); |
||||
} |
||||
|
||||
prePassRenderState.setFaceCullMode(RenderState.FaceCullMode.Back); |
||||
prePassRenderState.setColorWrite(false); |
||||
prePassRenderState.setDepthWrite(true); |
||||
prePassRenderState.setDepthTest(true); |
||||
prePassRenderState.setPolyOffset(0, 0); |
||||
|
||||
array.setAnisotropicFilter(1); |
||||
array.setShadowCompareMode(ShadowCompareMode.LessOrEqual); |
||||
|
||||
array.setMagFilter(MagFilter.Bilinear); |
||||
array.setMinFilter(MinFilter.BilinearNoMipMaps); |
||||
} |
||||
|
||||
public void displayDebug(AssetManager assetManager, Node guiRoot) { |
||||
guiRoot.addControl(new ShadowDebugControl(assetManager, this)); |
||||
} |
||||
|
||||
@Override |
||||
public void initialize(RenderManager rm, ViewPort vp) { |
||||
this.renderManager = rm; |
||||
this.viewPort = vp; |
||||
} |
||||
|
||||
public DirectionalShadowParameters directional() { |
||||
return directionalParams; |
||||
} |
||||
|
||||
public void setPolyOffset(float factor, float units) { |
||||
prePassRenderState.setPolyOffset(factor, units); |
||||
} |
||||
|
||||
public int getTextureSize() { |
||||
return textureSize; |
||||
} |
||||
|
||||
public void setTextureSize(int textureSize) { |
||||
// TODO: support changing texture size after shadow maps are created
|
||||
this.textureSize = textureSize; |
||||
} |
||||
|
||||
public TextureArray getShadowMapTexture() { |
||||
return array; |
||||
} |
||||
|
||||
public void addLight(Light light) { |
||||
if (array.getImage() == null) { |
||||
array.setImage(new Image( |
||||
Format.Depth32F, |
||||
textureSize, |
||||
textureSize, |
||||
0, |
||||
new ArrayList<ByteBuffer>(), |
||||
ColorSpace.Linear)); |
||||
} |
||||
|
||||
ShadowMap shadowMap; |
||||
switch (light.getType()) { |
||||
case Directional: |
||||
shadowMap = new DirectionalArrayShadowMap( |
||||
(DirectionalLight) light, |
||||
array, |
||||
nextArraySlice, |
||||
textureSize, |
||||
directionalParams.getNumSplits()); |
||||
break; |
||||
case Point: |
||||
shadowMap = new PointArrayShadowMap( |
||||
(PointLight) light, |
||||
array, |
||||
nextArraySlice, |
||||
textureSize); |
||||
break; |
||||
case Spot: |
||||
shadowMap = new SpotArrayShadowMap( |
||||
(SpotLight) light, |
||||
array, |
||||
nextArraySlice, |
||||
textureSize); |
||||
break; |
||||
default: |
||||
throw new UnsupportedOperationException(); |
||||
} |
||||
|
||||
shadowedLights.put(light, shadowMap); |
||||
nextArraySlice += shadowMap.getNumSlices(); |
||||
} |
||||
|
||||
@Override |
||||
public void reshape(ViewPort vp, int w, int h) { |
||||
} |
||||
|
||||
@Override |
||||
public boolean isInitialized() { |
||||
return this.viewPort != null; |
||||
} |
||||
|
||||
@Override |
||||
public void preFrame(float tpf) { |
||||
} |
||||
|
||||
private void renderShadowMaps(ViewPort viewPort) { |
||||
renderManager.setForcedRenderState(prePassRenderState); |
||||
renderManager.setForcedTechnique(PRE_SHADOW_TECHNIQUE_NAME); |
||||
renderManager.addForcedMatParam(pointLightOverride); |
||||
|
||||
for (int i = 0; i < shadowedLights.size(); i++) { |
||||
Light light = shadowedLights.getKey(i); |
||||
ShadowMap shadowMap = shadowedLights.getValue(i); |
||||
|
||||
TempVars vars = TempVars.get(); |
||||
try { |
||||
light.setFrustumCheckNeeded(false); |
||||
light.setIntersectsFrustum(light.intersectsFrustum(viewPort.getCamera(), vars)); |
||||
if (!light.isIntersectsFrustum()) { |
||||
continue; |
||||
} |
||||
} finally { |
||||
vars.release(); |
||||
} |
||||
|
||||
pointLightOverride.setEnabled(shadowMap.getLightType() == Type.Point); |
||||
|
||||
switch (shadowMap.getLightType()) { |
||||
case Directional: |
||||
DirectionalArrayShadowMap directionalShadow = (DirectionalArrayShadowMap) shadowMap; |
||||
directionalShadow.renderShadowMap(renderManager, viewPort, directionalParams, shadowCasters, points); |
||||
break; |
||||
case Point: |
||||
PointArrayShadowMap pointShadow = (PointArrayShadowMap) shadowMap; |
||||
pointShadow.renderShadowMap(renderManager, viewPort, shadowCasters); |
||||
break; |
||||
case Spot: |
||||
SpotArrayShadowMap spotShadow = (SpotArrayShadowMap) shadowMap; |
||||
spotShadow.renderShadowMap(renderManager, viewPort, shadowCasters); |
||||
break; |
||||
default: |
||||
throw new UnsupportedOperationException(); |
||||
} |
||||
|
||||
light.setShadowMap(shadowMap); |
||||
} |
||||
|
||||
Renderer renderer = renderManager.getRenderer(); |
||||
renderer.setFrameBuffer(viewPort.getOutputFrameBuffer()); |
||||
renderManager.removeForcedMatParam(pointLightOverride); |
||||
renderManager.setForcedRenderState(null); |
||||
renderManager.setForcedTechnique(null); |
||||
renderManager.setCamera(viewPort.getCamera(), false); |
||||
} |
||||
|
||||
@Override |
||||
public void postQueue(RenderQueue rq) { |
||||
directionalParams.updateSplitPositions(viewPort.getCamera()); |
||||
renderShadowMaps(viewPort); |
||||
} |
||||
|
||||
@Override |
||||
public void postFrame(FrameBuffer out) { |
||||
// TODO: call discard contents on all the framebuffers.
|
||||
for (int i = 0; i < shadowedLights.size(); i++) { |
||||
Light light = shadowedLights.getKey(i); |
||||
light.setShadowMap(null); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void cleanup() { |
||||
} |
||||
|
||||
@Override |
||||
public void setProfiler(AppProfiler profiler) { |
||||
} |
||||
|
||||
} |
@ -0,0 +1,183 @@ |
||||
/* |
||||
* Copyright (c) 2009-2016 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.shadow.next; |
||||
|
||||
import com.jme3.shadow.next.pssm.DirectionalShadowParameters; |
||||
import com.jme3.light.DirectionalLight; |
||||
import com.jme3.light.Light; |
||||
import com.jme3.material.RenderState; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.post.SceneProcessor; |
||||
import com.jme3.profile.AppProfiler; |
||||
import com.jme3.renderer.RenderManager; |
||||
import com.jme3.renderer.Renderer; |
||||
import com.jme3.renderer.ViewPort; |
||||
import com.jme3.renderer.queue.GeometryList; |
||||
import com.jme3.renderer.queue.OpaqueComparator; |
||||
import com.jme3.renderer.queue.RenderQueue; |
||||
import com.jme3.shadow.next.pssm.DirectionalShadowMap; |
||||
import com.jme3.texture.FrameBuffer; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* The 4th generation of shadow mapping in jME3. |
||||
* <p> |
||||
* This version is primarily focused on rendering in-pass shadows, so pre-pass |
||||
* and subsequent stages are separated. |
||||
* |
||||
* @author Kirill Vainer |
||||
*/ |
||||
public class PreShadowRenderer implements SceneProcessor { |
||||
|
||||
private static final String PRE_SHADOW_TECHNIQUE_NAME = "PreShadow"; |
||||
|
||||
private RenderManager renderManager; |
||||
private ViewPort viewPort; |
||||
private final Vector3f[] points = new Vector3f[8]; |
||||
private final GeometryList shadowCasters = new GeometryList(new OpaqueComparator()); |
||||
private final List<ShadowMap> shadowMaps = new ArrayList<>(); |
||||
private final RenderState prePassRenderState = RenderState.ADDITIONAL.clone(); |
||||
|
||||
private int textureSize = 1024; |
||||
|
||||
// parameters for directional lights
|
||||
private final DirectionalShadowParameters directionalParams = new DirectionalShadowParameters(); |
||||
|
||||
public PreShadowRenderer() { |
||||
for (int i = 0; i < points.length; i++) { |
||||
points[i] = new Vector3f(); |
||||
} |
||||
|
||||
prePassRenderState.setFaceCullMode(RenderState.FaceCullMode.Off); |
||||
prePassRenderState.setColorWrite(false); |
||||
prePassRenderState.setDepthWrite(true); |
||||
prePassRenderState.setDepthTest(true); |
||||
prePassRenderState.setPolyOffset(1.2f, 0); |
||||
} |
||||
|
||||
@Override |
||||
public void initialize(RenderManager rm, ViewPort vp) { |
||||
this.renderManager = rm; |
||||
this.viewPort = vp; |
||||
} |
||||
|
||||
public DirectionalShadowParameters directional() { |
||||
return directionalParams; |
||||
} |
||||
|
||||
public void setPolyOffset(float factor, float units) { |
||||
// TODO: might want to set this separately per model
|
||||
prePassRenderState.setPolyOffset(factor, units); |
||||
} |
||||
|
||||
public int getTextureSize() { |
||||
return textureSize; |
||||
} |
||||
|
||||
public void setTextureSize(int textureSize) { |
||||
// TODO: support changing texture size after shadow maps are created
|
||||
this.textureSize = textureSize; |
||||
} |
||||
|
||||
public void addLight(Light light) { |
||||
ShadowMap shadowMap; |
||||
switch (light.getType()) { |
||||
case Directional: |
||||
shadowMap = new DirectionalShadowMap( |
||||
(DirectionalLight) light, |
||||
textureSize, |
||||
directionalParams.getNumSplits(), |
||||
points); |
||||
break; |
||||
default: |
||||
throw new UnsupportedOperationException(); |
||||
} |
||||
|
||||
light.setShadowMap(shadowMap); |
||||
shadowMaps.add(shadowMap); |
||||
} |
||||
|
||||
@Override |
||||
public void reshape(ViewPort vp, int w, int h) { |
||||
} |
||||
|
||||
@Override |
||||
public boolean isInitialized() { |
||||
return this.viewPort != null; |
||||
} |
||||
|
||||
@Override |
||||
public void preFrame(float tpf) { |
||||
} |
||||
|
||||
private void renderShadowMaps() { |
||||
renderManager.setForcedRenderState(prePassRenderState); |
||||
renderManager.setForcedTechnique(PRE_SHADOW_TECHNIQUE_NAME); |
||||
|
||||
for (ShadowMap shadowMap : shadowMaps) { |
||||
switch (shadowMap.getLightType()) { |
||||
case Directional: |
||||
DirectionalShadowMap directionalShadow = (DirectionalShadowMap) shadowMap; |
||||
directionalShadow.renderShadowMap(renderManager, viewPort, directionalParams, shadowCasters); |
||||
break; |
||||
default: |
||||
throw new UnsupportedOperationException(); |
||||
} |
||||
} |
||||
|
||||
Renderer renderer = renderManager.getRenderer(); |
||||
renderer.setFrameBuffer(viewPort.getOutputFrameBuffer()); |
||||
renderManager.setForcedRenderState(null); |
||||
renderManager.setForcedTechnique(null); |
||||
renderManager.setCamera(viewPort.getCamera(), false); |
||||
} |
||||
|
||||
@Override |
||||
public void postQueue(RenderQueue rq) { |
||||
directionalParams.updateSplitPositions(viewPort.getCamera()); |
||||
renderShadowMaps(); |
||||
} |
||||
|
||||
@Override |
||||
public void postFrame(FrameBuffer out) { |
||||
} |
||||
|
||||
@Override |
||||
public void cleanup() { |
||||
} |
||||
|
||||
@Override |
||||
public void setProfiler(AppProfiler profiler) { |
||||
} |
||||
|
||||
} |
@ -0,0 +1,98 @@ |
||||
/* |
||||
* Copyright (c) 2009-2017 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.shadow.next; |
||||
|
||||
import com.jme3.asset.AssetManager; |
||||
import com.jme3.material.Material; |
||||
import com.jme3.renderer.RenderManager; |
||||
import com.jme3.renderer.ViewPort; |
||||
import com.jme3.scene.Node; |
||||
import com.jme3.scene.Spatial; |
||||
import com.jme3.scene.control.AbstractControl; |
||||
import com.jme3.texture.Image; |
||||
import com.jme3.texture.TextureArray; |
||||
import com.jme3.ui.Picture; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* Shows the shadow maps on the screen |
||||
* |
||||
* @author Kirill Vainer |
||||
*/ |
||||
final class ShadowDebugControl extends AbstractControl { |
||||
|
||||
private final List<Picture> pictures = new ArrayList<>(); |
||||
|
||||
public ShadowDebugControl(AssetManager assetManager, InPassShadowRenderer shadowRenderer) { |
||||
TextureArray shadowMapArray = shadowRenderer.getShadowMapTexture(); |
||||
Image shadowMap = shadowMapArray.getImage(); |
||||
for (int i = 0; i < shadowMap.getDepth(); i++) { |
||||
Picture picture = new Picture("Shadow Map " + i); |
||||
picture.setPosition(20, i * 128 + 20); |
||||
picture.setWidth(128); |
||||
picture.setHeight(128); |
||||
|
||||
Material material = new Material(assetManager, "Common/MatDefs/Shadow/ShowShadowArray.j3md"); |
||||
material.setTexture("ShadowMapArray", shadowMapArray); |
||||
material.setFloat("ShadowMapSlice", i); |
||||
picture.setMaterial(material); |
||||
|
||||
pictures.add(picture); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void setSpatial(Spatial spatial) { |
||||
if (spatial != null) { |
||||
for (Picture picture : pictures) { |
||||
((Node) spatial).detachChild(picture); |
||||
} |
||||
} |
||||
super.setSpatial(spatial); |
||||
if (spatial != null) { |
||||
for (Picture picture : pictures) { |
||||
((Node) spatial).attachChild(picture); |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
protected void controlUpdate(float tpf) { |
||||
|
||||
} |
||||
|
||||
@Override |
||||
protected void controlRender(RenderManager rm, ViewPort vp) { |
||||
} |
||||
|
||||
} |
@ -0,0 +1,50 @@ |
||||
/* |
||||
* Copyright (c) 2009-2016 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.shadow.next; |
||||
|
||||
import com.jme3.light.Light; |
||||
import com.jme3.light.Light.Type; |
||||
|
||||
/** |
||||
* Represents shadow information for a light. |
||||
* @param <T> Type of light |
||||
* @author Kirill Vainer |
||||
*/ |
||||
public interface ShadowMap<T extends Light> { |
||||
|
||||
public Type getLightType(); |
||||
|
||||
public int getNumSlices(); |
||||
|
||||
public ShadowMapSlice<T> getSlice(int index); |
||||
|
||||
} |
@ -0,0 +1,58 @@ |
||||
/* |
||||
* Copyright (c) 2009-2016 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.shadow.next; |
||||
|
||||
import com.jme3.light.Light; |
||||
import com.jme3.math.Matrix4f; |
||||
import com.jme3.renderer.RenderManager; |
||||
import com.jme3.renderer.ViewPort; |
||||
import com.jme3.renderer.queue.GeometryList; |
||||
|
||||
/** |
||||
* Represents a single slice of a shadow map. |
||||
* |
||||
* @param <T> Type of light |
||||
* |
||||
* @author Kirill Vainer |
||||
*/ |
||||
public interface ShadowMapSlice<T extends Light> { |
||||
|
||||
public static final Matrix4f BIAS_MATRIX = new Matrix4f( |
||||
0.5f, 0.0f, 0.0f, 0.5f, |
||||
0.0f, 0.5f, 0.0f, 0.5f, |
||||
0.0f, 0.0f, 0.5f, 0.5f, |
||||
0.0f, 0.0f, 0.0f, 1.0f); |
||||
|
||||
public Matrix4f getBiasedViewProjectionMatrix(); |
||||
|
||||
public void renderShadowMap(RenderManager renderManager, T light, ViewPort viewPort, GeometryList shadowCasters); |
||||
} |
@ -0,0 +1,47 @@ |
||||
/* |
||||
* Copyright (c) 2009-2016 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.shadow.next.array; |
||||
|
||||
import com.jme3.shadow.next.ShadowMap; |
||||
import com.jme3.texture.TextureArray; |
||||
|
||||
/** |
||||
* Represents shadow information for a light, uses texture arrays. |
||||
* |
||||
* @author Kirill Vainer |
||||
*/ |
||||
public interface ArrayShadowMap extends ShadowMap { |
||||
|
||||
public TextureArray getArray(); |
||||
|
||||
public int getFirstArraySlice(); |
||||
} |
@ -0,0 +1,41 @@ |
||||
/* |
||||
* Copyright (c) 2009-2016 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.shadow.next.array; |
||||
|
||||
import com.jme3.light.Light; |
||||
import com.jme3.shadow.next.ShadowMapSlice; |
||||
|
||||
/** |
||||
* @author Kirill Vainer |
||||
*/ |
||||
public interface ArrayShadowMapSlice<T extends Light> extends ShadowMapSlice<T> { |
||||
} |
@ -0,0 +1,75 @@ |
||||
/* |
||||
* Copyright (c) 2009-2016 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.shadow.next.array; |
||||
|
||||
import com.jme3.light.Light; |
||||
import com.jme3.texture.TextureArray; |
||||
|
||||
/** |
||||
* |
||||
* @author Kirill Vainer |
||||
*/ |
||||
public abstract class BaseArrayShadowMap<T extends ArrayShadowMapSlice> implements ArrayShadowMap { |
||||
|
||||
protected final TextureArray array; |
||||
protected final int firstArraySlice; |
||||
protected T[] slices; |
||||
|
||||
public BaseArrayShadowMap(TextureArray array, int firstArraySlice) { |
||||
this.array = array; |
||||
this.firstArraySlice = firstArraySlice; |
||||
} |
||||
|
||||
@Override |
||||
public TextureArray getArray() { |
||||
return array; |
||||
} |
||||
|
||||
@Override |
||||
public int getFirstArraySlice() { |
||||
return firstArraySlice; |
||||
} |
||||
|
||||
@Override |
||||
public abstract Light.Type getLightType(); |
||||
|
||||
@Override |
||||
public int getNumSlices() { |
||||
return slices.length; |
||||
} |
||||
|
||||
@Override |
||||
public T getSlice(int index) { |
||||
return slices[index]; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,98 @@ |
||||
/* |
||||
* Copyright (c) 2009-2016 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.shadow.next.array; |
||||
|
||||
import com.jme3.light.Light; |
||||
import com.jme3.math.Matrix4f; |
||||
import com.jme3.renderer.Camera; |
||||
import com.jme3.renderer.RenderManager; |
||||
import com.jme3.renderer.Renderer; |
||||
import com.jme3.renderer.ViewPort; |
||||
import com.jme3.renderer.queue.GeometryList; |
||||
import com.jme3.texture.FrameBuffer; |
||||
import com.jme3.texture.Image; |
||||
import com.jme3.texture.TextureArray; |
||||
|
||||
/** |
||||
* @param <T> |
||||
* @author Kirill Vainer |
||||
*/ |
||||
public class BaseArrayShadowMapSlice<T extends Light> implements ArrayShadowMapSlice<T> { |
||||
|
||||
protected final FrameBuffer frameBuffer; |
||||
protected final Camera shadowCamera; |
||||
protected final Matrix4f biasedViewProjectionMatrix = new Matrix4f(); |
||||
|
||||
protected boolean fbNeedClear = true; |
||||
|
||||
public BaseArrayShadowMapSlice(TextureArray array, int layer, int textureSize, boolean useBorder) { |
||||
this.shadowCamera = new Camera(textureSize, textureSize); |
||||
|
||||
if (useBorder) { |
||||
float onePx = 1f / textureSize; |
||||
this.shadowCamera.setViewPort(onePx, 1f - onePx, onePx, 1f - onePx); |
||||
} |
||||
|
||||
this.frameBuffer = new FrameBuffer(textureSize, textureSize, 1); |
||||
|
||||
Image image = array.getImage(); |
||||
image.setDepth(image.getDepth() + 1); |
||||
image.addData(null); |
||||
|
||||
this.frameBuffer.setDepthTexture(array, layer); |
||||
} |
||||
|
||||
@Override |
||||
public Matrix4f getBiasedViewProjectionMatrix() { |
||||
return biasedViewProjectionMatrix; |
||||
} |
||||
|
||||
@Override |
||||
public void renderShadowMap(RenderManager renderManager, Light light, ViewPort viewPort, GeometryList shadowCasters) { |
||||
Renderer renderer = renderManager.getRenderer(); |
||||
|
||||
if (fbNeedClear) { |
||||
renderer.setFrameBuffer(frameBuffer); |
||||
renderer.clearClipRect(); |
||||
renderer.clearBuffers(false, true, false); |
||||
fbNeedClear = false; |
||||
} |
||||
|
||||
if (shadowCasters.size() > 0) { |
||||
renderManager.setCamera(shadowCamera, false); |
||||
viewPort.getQueue().renderShadowQueue(shadowCasters, renderManager, shadowCamera, true); |
||||
fbNeedClear = true; |
||||
} |
||||
|
||||
BIAS_MATRIX.mult(shadowCamera.getViewProjectionMatrix(), biasedViewProjectionMatrix); |
||||
} |
||||
} |
@ -0,0 +1,81 @@ |
||||
/* |
||||
* Copyright (c) 2009-2016 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.shadow.next.array; |
||||
|
||||
import com.jme3.light.DirectionalLight; |
||||
import com.jme3.light.Light; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.renderer.RenderManager; |
||||
import com.jme3.renderer.ViewPort; |
||||
import com.jme3.renderer.queue.GeometryList; |
||||
import com.jme3.shadow.next.pssm.DirectionalShadowParameters; |
||||
import com.jme3.texture.TextureArray; |
||||
|
||||
/** |
||||
* @author Kirill Vainer |
||||
*/ |
||||
public class DirectionalArrayShadowMap extends BaseArrayShadowMap<DirectionalArrayShadowMapSlice> { |
||||
|
||||
private final DirectionalLight light; |
||||
private final Vector3f projectionSplitPositions = new Vector3f(); |
||||
|
||||
public DirectionalArrayShadowMap(DirectionalLight light, TextureArray array, int firstArraySlice, int textureSize, int numSplits) { |
||||
super(array, firstArraySlice); |
||||
this.light = light; |
||||
this.slices = new DirectionalArrayShadowMapSlice[numSplits]; |
||||
for (int i = 0; i < numSplits; i++) { |
||||
this.slices[i] = new DirectionalArrayShadowMapSlice(array, firstArraySlice + i, textureSize); |
||||
} |
||||
} |
||||
|
||||
public void renderShadowMap(RenderManager renderManager, ViewPort viewPort, DirectionalShadowParameters params, GeometryList shadowCasters, Vector3f[] points) { |
||||
projectionSplitPositions.set(params.getProjectionSplitPositions()); |
||||
float[] splitPositionsViewSpace = params.getSplitPositions(); |
||||
for (int i = 0; i < slices.length; i++) { |
||||
float near = splitPositionsViewSpace[i]; |
||||
float far = splitPositionsViewSpace[i + 1]; |
||||
shadowCasters.clear(); |
||||
slices[i].updateShadowCamera(viewPort, light, shadowCasters, near, far, points); |
||||
slices[i].renderShadowMap(renderManager, light, viewPort, shadowCasters); |
||||
} |
||||
} |
||||
|
||||
public Vector3f getProjectionSplitPositions() { |
||||
return projectionSplitPositions; |
||||
} |
||||
|
||||
@Override |
||||
public Light.Type getLightType() { |
||||
return Light.Type.Directional; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,75 @@ |
||||
/* |
||||
* Copyright (c) 2009-2016 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.shadow.next.array; |
||||
|
||||
import com.jme3.light.DirectionalLight; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.renderer.ViewPort; |
||||
import com.jme3.renderer.queue.GeometryList; |
||||
import com.jme3.shadow.ShadowUtil; |
||||
import com.jme3.texture.TextureArray; |
||||
|
||||
/** |
||||
* @author Kirill Vainer |
||||
*/ |
||||
public class DirectionalArrayShadowMapSlice extends BaseArrayShadowMapSlice<DirectionalLight> { |
||||
|
||||
public DirectionalArrayShadowMapSlice(TextureArray array, int layer, int textureSize) { |
||||
super(array, layer, textureSize, true); |
||||
this.shadowCamera.setParallelProjection(true); |
||||
} |
||||
|
||||
private static boolean isParallelToYUp(Vector3f direction) { |
||||
return direction.x == 0 && direction.z == 0 |
||||
&& (direction.y == -1 || direction.y == 1); |
||||
} |
||||
|
||||
public void updateShadowCamera( |
||||
ViewPort viewPort, |
||||
DirectionalLight light, |
||||
GeometryList shadowCasters, |
||||
float near, |
||||
float far, |
||||
Vector3f[] points) { |
||||
if (isParallelToYUp(light.getDirection())) { |
||||
// direction and up cannot be parallel
|
||||
shadowCamera.lookAtDirection(light.getDirection(), Vector3f.UNIT_Z); |
||||
} else { |
||||
shadowCamera.lookAtDirection(light.getDirection(), Vector3f.UNIT_Y); |
||||
} |
||||
|
||||
int textureSize = frameBuffer.getWidth(); |
||||
ShadowUtil.updateFrustumPoints(viewPort.getCamera(), near, far, points); |
||||
ShadowUtil.updateShadowCamera(viewPort, null, shadowCamera, points, shadowCasters, textureSize); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,99 @@ |
||||
/* |
||||
* Copyright (c) 2009-2017 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.shadow.next.array; |
||||
|
||||
import com.jme3.light.Light; |
||||
import com.jme3.light.Light.Type; |
||||
import com.jme3.light.PointLight; |
||||
import com.jme3.math.Quaternion; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.renderer.RenderManager; |
||||
import com.jme3.renderer.ViewPort; |
||||
import com.jme3.renderer.queue.GeometryList; |
||||
import com.jme3.texture.TextureArray; |
||||
|
||||
/** |
||||
* @author Kirill Vainer |
||||
*/ |
||||
public class PointArrayShadowMap extends BaseArrayShadowMap<PointArrayShadowMapSlice> { |
||||
|
||||
private final PointLight light; |
||||
|
||||
private static final Quaternion[] ROTATIONS = new Quaternion[6]; |
||||
|
||||
static { |
||||
for (int i = 0; i < ROTATIONS.length; i++) { |
||||
ROTATIONS[i] = new Quaternion(); |
||||
} |
||||
|
||||
// left
|
||||
ROTATIONS[0].fromAxes(Vector3f.UNIT_Z, Vector3f.UNIT_Y, Vector3f.UNIT_X.mult(-1f)); |
||||
|
||||
// right
|
||||
ROTATIONS[1].fromAxes(Vector3f.UNIT_Z.mult(-1f), Vector3f.UNIT_Y, Vector3f.UNIT_X); |
||||
|
||||
// bottom
|
||||
ROTATIONS[2].fromAxes(Vector3f.UNIT_X.mult(-1f), Vector3f.UNIT_Z.mult(-1f), Vector3f.UNIT_Y.mult(-1f)); |
||||
|
||||
// top
|
||||
ROTATIONS[3].fromAxes(Vector3f.UNIT_X.mult(-1f), Vector3f.UNIT_Z, Vector3f.UNIT_Y); |
||||
|
||||
// forward
|
||||
ROTATIONS[4].fromAxes(Vector3f.UNIT_X.mult(-1f), Vector3f.UNIT_Y, Vector3f.UNIT_Z.mult(-1f)); |
||||
|
||||
// backward
|
||||
ROTATIONS[5].fromAxes(Vector3f.UNIT_X, Vector3f.UNIT_Y, Vector3f.UNIT_Z); |
||||
} |
||||
|
||||
public PointArrayShadowMap(PointLight light, TextureArray array, int firstArraySlice, int textureSize) { |
||||
super(array, firstArraySlice); |
||||
this.light = light; |
||||
this.slices = new PointArrayShadowMapSlice[6]; |
||||
for (int i = 0; i < slices.length; i++) { |
||||
this.slices[i] = new PointArrayShadowMapSlice(array, firstArraySlice + i, textureSize, ROTATIONS[i]); |
||||
} |
||||
} |
||||
|
||||
public void renderShadowMap(RenderManager renderManager, ViewPort viewPort, GeometryList shadowCasters) { |
||||
for (int i = 0; i < slices.length; i++) { |
||||
shadowCasters.clear(); |
||||
slices[i].updateShadowCamera(viewPort, light, shadowCasters); |
||||
slices[i].renderShadowMap(renderManager, light, viewPort, shadowCasters); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public Light.Type getLightType() { |
||||
return Type.Point; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,60 @@ |
||||
/* |
||||
* Copyright (c) 2009-2017 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.shadow.next.array; |
||||
|
||||
import com.jme3.light.PointLight; |
||||
import com.jme3.math.Quaternion; |
||||
import com.jme3.renderer.ViewPort; |
||||
import com.jme3.renderer.queue.GeometryList; |
||||
import com.jme3.renderer.queue.RenderQueue.ShadowMode; |
||||
import com.jme3.scene.Spatial; |
||||
import com.jme3.shadow.ShadowUtil; |
||||
import com.jme3.texture.TextureArray; |
||||
|
||||
/** |
||||
* @author Kirill Vainer |
||||
*/ |
||||
public class PointArrayShadowMapSlice extends BaseArrayShadowMapSlice<PointLight> { |
||||
|
||||
public PointArrayShadowMapSlice(TextureArray array, int layer, int textureSize, Quaternion axes) { |
||||
super(array, layer, textureSize, false); |
||||
shadowCamera.setAxes(axes); |
||||
} |
||||
|
||||
public void updateShadowCamera(ViewPort viewPort, PointLight light, GeometryList shadowCasters) { |
||||
shadowCamera.setFrustumPerspective(90f, 1f, 0.5f, light.getRadius()); |
||||
shadowCamera.setLocation(light.getPosition()); |
||||
for (Spatial scene : viewPort.getScenes()) { |
||||
ShadowUtil.getGeometriesInCamFrustum(scene, shadowCamera, ShadowMode.Cast, shadowCasters); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,66 @@ |
||||
/* |
||||
* Copyright (c) 2009-2016 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.shadow.next.array; |
||||
|
||||
import com.jme3.light.Light; |
||||
import com.jme3.light.SpotLight; |
||||
import com.jme3.renderer.RenderManager; |
||||
import com.jme3.renderer.ViewPort; |
||||
import com.jme3.renderer.queue.GeometryList; |
||||
import com.jme3.texture.TextureArray; |
||||
|
||||
/** |
||||
* @author Kirill Vainer |
||||
*/ |
||||
public class SpotArrayShadowMap extends BaseArrayShadowMap<SpotArrayShadowMapSlice> { |
||||
|
||||
private final SpotLight light; |
||||
|
||||
public SpotArrayShadowMap(SpotLight light, TextureArray array, int firstArraySlice, int textureSize) { |
||||
super(array, firstArraySlice); |
||||
this.light = light; |
||||
slices = new SpotArrayShadowMapSlice[]{ |
||||
new SpotArrayShadowMapSlice(array, firstArraySlice, textureSize) |
||||
}; |
||||
} |
||||
|
||||
public void renderShadowMap(RenderManager renderManager, ViewPort viewPort, GeometryList shadowCasters) { |
||||
shadowCasters.clear(); |
||||
slices[0].updateShadowCamera(viewPort, light, shadowCasters); |
||||
slices[0].renderShadowMap(renderManager, light, viewPort, shadowCasters); |
||||
} |
||||
|
||||
@Override |
||||
public Light.Type getLightType() { |
||||
return Light.Type.Spot; |
||||
} |
||||
} |
@ -0,0 +1,71 @@ |
||||
/* |
||||
* Copyright (c) 2009-2016 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.shadow.next.array; |
||||
|
||||
import com.jme3.light.SpotLight; |
||||
import com.jme3.math.FastMath; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.renderer.ViewPort; |
||||
import com.jme3.renderer.queue.GeometryList; |
||||
import com.jme3.renderer.queue.RenderQueue; |
||||
import com.jme3.scene.Spatial; |
||||
import com.jme3.shadow.ShadowUtil; |
||||
import com.jme3.texture.TextureArray; |
||||
|
||||
/** |
||||
* @author Kirill Vainer |
||||
*/ |
||||
public class SpotArrayShadowMapSlice extends BaseArrayShadowMapSlice<SpotLight> { |
||||
|
||||
public SpotArrayShadowMapSlice(TextureArray array, int layer, int textureSize) { |
||||
super(array, layer, textureSize, true); |
||||
} |
||||
|
||||
private static boolean isParallelToYUp(Vector3f direction) { |
||||
return direction.x == 0 && direction.z == 0 |
||||
&& (direction.y == -1 || direction.y == 1); |
||||
} |
||||
|
||||
public void updateShadowCamera(ViewPort viewPort, SpotLight light, GeometryList shadowCasters) { |
||||
shadowCamera.setLocation(light.getPosition()); |
||||
if (isParallelToYUp(light.getDirection())) { |
||||
// direction and up cannot be parallel
|
||||
shadowCamera.lookAtDirection(light.getDirection(), Vector3f.UNIT_Z); |
||||
} else { |
||||
shadowCamera.lookAtDirection(light.getDirection(), Vector3f.UNIT_Y); |
||||
} |
||||
shadowCamera.setFrustumPerspective(light.getSpotOuterAngle() * FastMath.RAD_TO_DEG * 2.0f, 1, 1, light.getSpotRange()); |
||||
for (Spatial scene : viewPort.getScenes()) { |
||||
ShadowUtil.getGeometriesInCamFrustum(scene, shadowCamera, RenderQueue.ShadowMode.Cast, shadowCasters); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,87 @@ |
||||
/* |
||||
* Copyright (c) 2009-2016 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.shadow.next.pssm; |
||||
|
||||
import com.jme3.light.Light; |
||||
import com.jme3.math.Matrix4f; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.renderer.Camera; |
||||
import com.jme3.renderer.RenderManager; |
||||
import com.jme3.renderer.Renderer; |
||||
import com.jme3.renderer.ViewPort; |
||||
import com.jme3.renderer.queue.GeometryList; |
||||
import com.jme3.texture.FrameBuffer; |
||||
import com.jme3.texture.Image; |
||||
import com.jme3.texture.Texture.MagFilter; |
||||
import com.jme3.texture.Texture.MinFilter; |
||||
import com.jme3.texture.Texture.ShadowCompareMode; |
||||
import com.jme3.texture.Texture2D; |
||||
import com.jme3.shadow.next.ShadowMapSlice; |
||||
|
||||
public abstract class BaseShadowMapSlice<T extends Light> implements ShadowMapSlice<T> { |
||||
|
||||
protected final FrameBuffer frameBuffer; |
||||
protected final Texture2D depthTexture; |
||||
protected final Camera shadowCamera; |
||||
protected final Vector3f[] points; |
||||
protected final Matrix4f biasedViewProjectionMatrix = new Matrix4f(); |
||||
|
||||
public BaseShadowMapSlice(int size, Vector3f[] points) { |
||||
this.depthTexture = new Texture2D(size, size, Image.Format.Depth16); |
||||
this.depthTexture.setAnisotropicFilter(1); |
||||
this.depthTexture.setShadowCompareMode(ShadowCompareMode.LessOrEqual); |
||||
this.depthTexture.setMagFilter(MagFilter.Bilinear); |
||||
this.depthTexture.setMinFilter(MinFilter.BilinearNoMipMaps); |
||||
this.shadowCamera = new Camera(size, size); |
||||
this.frameBuffer = new FrameBuffer(size, size, 1); |
||||
this.frameBuffer.setDepthTexture(depthTexture); |
||||
this.points = points; |
||||
} |
||||
|
||||
@Override |
||||
public void renderShadowMap(RenderManager renderManager, T light, ViewPort viewPort, GeometryList shadowCasters) { |
||||
Renderer renderer = renderManager.getRenderer(); |
||||
|
||||
renderer.setFrameBuffer(frameBuffer); |
||||
renderer.clearBuffers(false, true, false); |
||||
|
||||
if (shadowCasters.size() > 0) { |
||||
renderManager.setCamera(shadowCamera, false); |
||||
viewPort.getQueue().renderShadowQueue(shadowCasters, renderManager, shadowCamera, true); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public Matrix4f getBiasedViewProjectionMatrix() { |
||||
return BIAS_MATRIX.mult(shadowCamera.getViewProjectionMatrix(), biasedViewProjectionMatrix); |
||||
} |
||||
} |
@ -0,0 +1,92 @@ |
||||
/* |
||||
* Copyright (c) 2009-2016 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.shadow.next.pssm; |
||||
|
||||
import com.jme3.light.DirectionalLight; |
||||
import com.jme3.light.Light.Type; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.math.Vector4f; |
||||
import com.jme3.renderer.RenderManager; |
||||
import com.jme3.renderer.ViewPort; |
||||
import com.jme3.renderer.queue.GeometryList; |
||||
import com.jme3.shadow.next.ShadowMapSlice; |
||||
import com.jme3.shadow.next.ShadowMap; |
||||
|
||||
/** |
||||
* @author Kirill Vainer |
||||
*/ |
||||
public class DirectionalShadowMap implements ShadowMap<DirectionalLight> { |
||||
|
||||
private final DirectionalLight light; |
||||
private final DirectionalShadowMapSlice[] splits; |
||||
private final Vector3f projectionSplitPositions = new Vector3f(); |
||||
|
||||
public DirectionalShadowMap(DirectionalLight light, int textureSize, int numSplits, Vector3f[] points) { |
||||
this.light = light; |
||||
this.splits = new DirectionalShadowMapSlice[numSplits]; |
||||
for (int i = 0; i < splits.length; i++) { |
||||
this.splits[i] = new DirectionalShadowMapSlice(textureSize, points); |
||||
} |
||||
} |
||||
|
||||
public void renderShadowMap(RenderManager renderManager, ViewPort viewPort, DirectionalShadowParameters params, GeometryList shadowCasters) { |
||||
projectionSplitPositions.set(params.getProjectionSplitPositions()); |
||||
float[] splitPositionsViewSpace = params.getSplitPositions(); |
||||
for (int i = 0; i < splits.length; i++) { |
||||
float near = splitPositionsViewSpace[i]; |
||||
float far = splitPositionsViewSpace[i + 1]; |
||||
shadowCasters.clear(); |
||||
splits[i].updateShadowCamera(viewPort, light, shadowCasters, near, far); |
||||
splits[i].renderShadowMap(renderManager, light, viewPort, shadowCasters); |
||||
} |
||||
} |
||||
|
||||
public Vector3f getProjectionSplitPositions() { |
||||
return projectionSplitPositions; |
||||
} |
||||
|
||||
@Override |
||||
public int getNumSlices() { |
||||
return splits.length; |
||||
} |
||||
|
||||
@Override |
||||
public ShadowMapSlice getSlice(int index) { |
||||
return splits[index]; |
||||
} |
||||
|
||||
@Override |
||||
public Type getLightType() { |
||||
return Type.Directional; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,67 @@ |
||||
/* |
||||
* Copyright (c) 2009-2016 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.shadow.next.pssm; |
||||
|
||||
import com.jme3.light.DirectionalLight; |
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.renderer.ViewPort; |
||||
import com.jme3.renderer.queue.GeometryList; |
||||
import com.jme3.shadow.ShadowUtil; |
||||
import com.jme3.texture.Texture2D; |
||||
|
||||
/** |
||||
* @author Kirill Vainer |
||||
*/ |
||||
public class DirectionalShadowMapSlice extends BaseShadowMapSlice<DirectionalLight> { |
||||
|
||||
public DirectionalShadowMapSlice(int size, Vector3f[] points) { |
||||
super(size, points); |
||||
this.shadowCamera.setParallelProjection(true); |
||||
} |
||||
|
||||
public void updateShadowCamera( |
||||
ViewPort viewPort, |
||||
DirectionalLight light, |
||||
GeometryList shadowCasters, |
||||
float near, |
||||
float far) { |
||||
ShadowUtil.updateFrustumPoints(viewPort.getCamera(), near, far, points); |
||||
shadowCamera.lookAtDirection(light.getDirection(), shadowCamera.getUp()); |
||||
|
||||
int textureSize = frameBuffer.getWidth(); |
||||
ShadowUtil.updateShadowCamera(viewPort, null, shadowCamera, points, shadowCasters, textureSize); |
||||
} |
||||
|
||||
public Texture2D getTexture() { |
||||
return depthTexture; |
||||
} |
||||
} |
@ -0,0 +1,139 @@ |
||||
/* |
||||
* Copyright (c) 2009-2016 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package com.jme3.shadow.next.pssm; |
||||
|
||||
import com.jme3.math.Vector3f; |
||||
import com.jme3.renderer.Camera; |
||||
import com.jme3.shadow.PssmShadowUtil; |
||||
|
||||
/** |
||||
* @author Kirill Vainer |
||||
*/ |
||||
public final class DirectionalShadowParameters { |
||||
|
||||
private float lambda = 0.65f; |
||||
private int numSplits = 4; |
||||
protected float zFarOverride = 0; |
||||
private float[] splitPositions = new float[numSplits + 1]; |
||||
private final Vector3f projectionSplitPositions = new Vector3f(); |
||||
|
||||
public float getLambda() { |
||||
return lambda; |
||||
} |
||||
|
||||
public void setLambda(float lambda) { |
||||
this.lambda = lambda; |
||||
} |
||||
|
||||
public int getNumSplits() { |
||||
return numSplits; |
||||
} |
||||
|
||||
public void setNumSplits(int numSplits) { |
||||
if (numSplits < 1 || numSplits > 4) { |
||||
throw new IllegalArgumentException("Number of splits must be between 1 and 4"); |
||||
} |
||||
this.numSplits = numSplits; |
||||
this.splitPositions = new float[numSplits + 1]; |
||||
} |
||||
|
||||
public float[] getSplitPositions() { |
||||
return splitPositions; |
||||
} |
||||
|
||||
public Vector3f getProjectionSplitPositions() { |
||||
return projectionSplitPositions; |
||||
} |
||||
|
||||
/** |
||||
* How far the shadows are rendered in the view |
||||
* |
||||
* @see #setShadowZExtend(float zFar) |
||||
* @return shadowZExtend |
||||
*/ |
||||
public float getShadowZExtend() { |
||||
return zFarOverride; |
||||
} |
||||
|
||||
/** |
||||
* Set the distance from the eye where the shadows will be rendered. |
||||
* |
||||
* The default value is dynamically computed based on the shadow |
||||
* casters/receivers union bound zFar, capped to view frustum far value. |
||||
* |
||||
* @param zFar the zFar values that override the computed one |
||||
*/ |
||||
public void setShadowZExtend(float zFar) { |
||||
this.zFarOverride = zFar; |
||||
|
||||
// TODO: Fade length not supported yet
|
||||
// if (zFarOverride == 0) {
|
||||
// fadeInfo = null;
|
||||
// frustumCam = null;
|
||||
// } else {
|
||||
// if (fadeInfo != null) {
|
||||
// fadeInfo.set(zFarOverride - fadeLength, 1f / fadeLength);
|
||||
// }
|
||||
// if (frustumCam == null && viewPort != null) {
|
||||
// initFrustumCam();
|
||||
// }
|
||||
// }
|
||||
} |
||||
|
||||
public void updateSplitPositions(Camera viewCamera) { |
||||
float near = viewCamera.getFrustumNear(); |
||||
float far = zFarOverride == 0f ? viewCamera.getFrustumFar() : zFarOverride; |
||||
|
||||
PssmShadowUtil.updateFrustumSplits(splitPositions, near, far, lambda); |
||||
|
||||
// TODO: Parallel projection can have negative near value, so split
|
||||
// positions must be adjusted.
|
||||
// if (viewCamera.isParallelProjection()) {
|
||||
// for (int i = 0; i < splitPositions.length; i++) {
|
||||
// splitPositions[i] = splitPositions[i] / (far - near);
|
||||
// }
|
||||
// }
|
||||
|
||||
switch (splitPositions.length) { |
||||
case 5: |
||||
// projectionSplitPositions.w = 1.0f;
|
||||
case 4: |
||||
projectionSplitPositions.z = viewCamera.getViewToProjectionZ(splitPositions[3]); |
||||
case 3: |
||||
projectionSplitPositions.y = viewCamera.getViewToProjectionZ(splitPositions[2]); |
||||
case 2: |
||||
case 1: |
||||
projectionSplitPositions.x = viewCamera.getViewToProjectionZ(splitPositions[1]); |
||||
break; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,15 @@ |
||||
#import "Common/ShaderLib/GLSLCompat.glsllib" |
||||
|
||||
uniform float m_ShadowMapSlice; |
||||
uniform sampler2DArray m_ShadowMapArray; |
||||
varying vec2 texCoord1; |
||||
|
||||
void main() { |
||||
float shadow = texture2D(m_ShadowMapArray, vec3(texCoord1, m_ShadowMapSlice)).r; |
||||
|
||||
shadow = sqrt(shadow); |
||||
|
||||
// TODO: make it betterer |
||||
gl_FragColor.rgb = vec3(shadow); |
||||
gl_FragColor.a = 1.0; |
||||
} |
@ -0,0 +1,17 @@ |
||||
MaterialDef Pre Shadow { |
||||
|
||||
MaterialParameters { |
||||
TextureArray ShadowMapArray |
||||
Float ShadowMapSlice |
||||
} |
||||
|
||||
Technique { |
||||
VertexShader GLSL150 : Common/MatDefs/Misc/Unshaded.vert |
||||
FragmentShader GLSL150 : Common/MatDefs/Shadow/ShowShadowArray.frag |
||||
|
||||
WorldParameters { |
||||
WorldViewProjectionMatrix |
||||
ViewProjectionMatrix |
||||
} |
||||
} |
||||
} |
@ -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 |
@ -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()); |
||||
} |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue