📄 shadowedrenderpass.java
字号:
/*
* Copyright (c) 2003-2009 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.jme.renderer.pass;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
import com.jme.light.Light;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.scene.Geometry;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jme.scene.TriMesh;
import com.jme.scene.shadow.MeshShadows;
import com.jme.scene.shadow.ShadowVolume;
import com.jme.scene.shape.Quad;
import com.jme.scene.state.BlendState;
import com.jme.scene.state.ColorMaskState;
import com.jme.scene.state.CullState;
import com.jme.scene.state.LightState;
import com.jme.scene.state.RenderState;
import com.jme.scene.state.StencilState;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.ZBufferState;
import com.jme.scene.state.StencilState.StencilFunction;
import com.jme.scene.state.StencilState.StencilOperation;
import com.jme.system.DisplaySystem;
/**
* <code>ShadowedRenderPass</code> is a render pass that renders the added
* spatials along with shadows cast by givens occluders and lights flagged as
* casting shadows.
*
* @author Mike Talbot (some code for MODULATIVE method written Jan 2005)
* @author Joshua Slack
* @version $Id: ShadowedRenderPass.java,v 1.16 2006/11/16 16:55:52 nca Exp $
*/
public class ShadowedRenderPass extends Pass {
private static final long serialVersionUID = 1L;
public enum LightingMethod {
/**
* value for lightingMethod indicating that a scene should be rendered first
* with ambient lighting and then multiple passes per light done to
* illuminate unshadowed areas (resulting in shadows.) More costly but more
* accurate than Modulative.
*/
Additive,
/**
* value for lightingMethod indicating that a scene should be rendered first
* with full lighting and then multiple screens applied per light to darken
* shadowed areas. More prone to artifacts than Additive, but faster.
*/
Modulative;
}
/** list of occluders registered with this pass. */
protected ArrayList<Spatial> occluders = new ArrayList<Spatial>();
/** node used to gather and hold shadow volumes for rendering. */
protected Node volumeNode = new Node("Volumes");
/** whether or not the renderstates for this pass have been init'd yet. */
protected boolean initialised = false;
/**
* A quad to use with MODULATIVE lightMethod for full screen darkening
* against the shadow stencil.
*/
protected Quad shadowQuad = new Quad("RenderForeground", 10, 10);
/**
* Used with MODULATIVE lightMethod. Defines the base color of the shadow -
* the alpha value is replaced with 1 - the alpha of the light's alpha.
*/
protected ColorRGBA shadowColor = new ColorRGBA(.2f,.2f,.2f,.1f);
/** Whether shadow volumes are visible */
protected boolean renderVolume = false;
/** Whether to render shadows (true) or act like a normal RenderPass (false) */
protected boolean renderShadows = true;
/** Sets the type of pass to do to show shadows - ADDITIVE or MODULATIVE */
protected LightingMethod lightingMethod = LightingMethod.Additive;
/** collection of TriMesh to MeshShadows mappings */
protected IdentityHashMap<TriMesh, MeshShadows> meshes = new IdentityHashMap<TriMesh, MeshShadows>();
/**
* list of occluders that will be casting shadows in this pass. If no
* occluders set, pass acts like normal RenderPass.
*/
protected ArrayList<TriMesh> occluderMeshes = new ArrayList<TriMesh>();
/**
* list of lights that will be used to calculate shadows in this pass.
* Constructed dynamically by searching through the scene for lights with
* shadowCaster set to true.
*/
protected ArrayList<Light> shadowLights = new ArrayList<Light>();
/**
* a place to internally save previous enforced states setup before
* rendering this pass
*/
protected RenderState[] preStates = new RenderState[RenderState.StateType.values().length];
protected int quadWidth = -1, quadHeight = -1;
private ShadowGate shadowGate = new DefaultShadowGate();
public static boolean rTexture = true;
/**
* <code>addOccluder</code> adds an occluder to this pass.
*
* @param toAdd
* Occluder Spatial to add to this pass.
*/
public void addOccluder(Spatial toAdd) {
occluders.add(toAdd);
}
/**
* <code>clearOccluders</code> removes all occluders from this pass.
*/
public void clearOccluders() {
occluders.clear();
}
/**
* <code>containsOccluder</code>
*
* @param s
* @return
*/
public boolean containsOccluder(Spatial s) {
return occluders.contains(s);
}
/**
* <code>removeOccluder</code>
*
* @param toRemove the Occluder Spatial to remove from this pass.
* @return true if the Spatial was found and removed.
*/
public boolean removeOccluder(Spatial toRemove) {
return occluders.remove(toRemove);
}
/**
* @return the number of occluders registered with this pass
*/
public int occludersSize() {
return occluders.size();
}
/**
* @return Returns whether shadow volumes will be rendered to the display.
*/
public boolean getRenderVolume() {
return renderVolume;
}
/**
* @param renderVolume
* sets whether shadow volumes will be rendered to the display
*/
public void setRenderVolume(boolean renderVolume) {
this.renderVolume = renderVolume;
}
/**
* @return whether shadow volumes will be rendered to the display.
*/
public boolean getRenderShadows() {
return renderShadows;
}
/**
* @param renderShadows
* whether shadows will be rendered by this pass.
*/
public void setRenderShadows(boolean renderShadows) {
this.renderShadows = renderShadows;
}
/**
* @return the shadowColor used by MODULATIVE lightMethod.
*/
public ColorRGBA getShadowColor() {
return shadowColor;
}
/**
* @param shadowColor
* the shadowColor used by MODULATIVE lightMethod.
*/
public void setShadowColor(ColorRGBA shadowColor) {
if (shadowColor == null)
throw new IllegalArgumentException("shadowColor must not be null!");
this.shadowColor = shadowColor;
}
/**
* @return the lightingMethod currently in use.
*/
public LightingMethod getLightingMethod() {
return lightingMethod;
}
/**
* Sets which method to use with the shadow volume stencils in order to
* generate shadows in the scene. See javadoc descriptions in the enum
* LightingMethod for more info.
*
* @param method
* method to use
* @throws IllegalArgumentException
* if method is null
*/
public void setLightingMethod(LightingMethod method) {
if (method == null) {
throw new IllegalArgumentException("method can not be null.");
}
this.lightingMethod = method;
}
/**
* <code>doRender</code> renders this pass to the framebuffer
*
* @param r
* Renderer to use for drawing.
* @see com.jme.renderer.pass.Pass#doRender(com.jme.renderer.Renderer)
*/
public void doRender(Renderer r) {
// init states
init();
if (!renderShadows) {
renderScene(r);
if (renderVolume) {
getShadowLights();
setupOccluderMeshes();
generateVolumes();
drawVolumes(r);
}
return;
}
// grab the shadowcasting lights
getShadowLights();
// grab the occluders
setupOccluderMeshes();
// if no occluders or no shadow casting lights, just render the scene normally and return.
if (occluderMeshes.size() == 0 || shadowLights.size() == 0) {
//render normal
renderScene(r);
cleanup();
return;
}
// otherwise render an ambient pass by masking the diffuse and specular of shadowcasting lights.
if (lightingMethod == LightingMethod.Additive) {
maskShadowLights(LightState.MASK_DIFFUSE | LightState.MASK_SPECULAR);
saveEnforcedStates();
context.enforceState(noTexture);
renderScene(r);
replaceEnforcedStates();
unmaskShadowLights();
r.setPolygonOffset(0.0f, -5.0f);
} else {
renderScene(r);
}
generateVolumes();
for (int l = shadowLights.size(); --l >= 0;) {
Light light = shadowLights.get(l);
light.setEnabled(false);
}
for (int l = shadowLights.size(); --l >= 0;) {
Light light = shadowLights.get(l);
// Clear out the stencil buffer
r.clearStencilBuffer();
light.setEnabled(true);
saveEnforcedStates();
context.enforceState(noTexture);
context.enforceState(forTesting);
context.enforceState(colorDisabled);
if (StencilState.supportsTwoSided()) {
context.enforceState(noCull);
context.enforceState(stencilBothFaces);
volumeNode.getChildren().clear();
addShadowVolumes(light);
volumeNode.updateWorldVectors();
volumeNode.onDraw(r);
} else {
context.enforceState(stencilFrontFaces);
context.enforceState(cullBackFace);
volumeNode.getChildren().clear();
addShadowVolumes(light);
volumeNode.updateWorldVectors();
volumeNode.onDraw(r);
context.enforceState(stencilBackFaces);
context.enforceState(cullFrontFace);
volumeNode.onDraw(r);
}
context.enforceState(colorEnabled);
context.enforceState(forColorPassTesting);
context.enforceState(cullBackFace);
if (lightingMethod == LightingMethod.Additive) {
context.enforceState(lights);
context.enforceState(blended);
lights.detachAll();
lights.attach(light);
context.enforceState(stencilDrawWhenNotSet);
renderScene(r);
} else {
if (rTexture) {
context.enforceState(modblended);
context.enforceState(zbufferAlways);
context.enforceState(cullBackFace);
context.enforceState(noLights);
context.enforceState(stencilDrawOnlyWhenSet);
shadowColor.a = 1 - light.getAmbient().a;
shadowQuad.setDefaultColor(shadowColor);
r.setOrtho();
resetShadowQuad(r);
shadowQuad.draw(r);
r.unsetOrtho();
}
}
light.setEnabled(false);
replaceEnforcedStates();
}
for (int l = shadowLights.size(); --l >= 0;) {
Light light = shadowLights.get(l);
light.setEnabled(true);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -