1 /* 2 * Copyright (c) 2009-2010 jMonkeyEngine 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package com.jme3.shadow; 33 34 import com.jme3.asset.AssetManager; 35 import com.jme3.material.Material; 36 import com.jme3.math.Vector3f; 37 import com.jme3.post.SceneProcessor; 38 import com.jme3.renderer.Camera; 39 import com.jme3.renderer.RenderManager; 40 import com.jme3.renderer.Renderer; 41 import com.jme3.renderer.ViewPort; 42 import com.jme3.renderer.queue.GeometryList; 43 import com.jme3.renderer.queue.RenderQueue; 44 import com.jme3.renderer.queue.RenderQueue.ShadowMode; 45 import com.jme3.texture.FrameBuffer; 46 import com.jme3.texture.Image.Format; 47 import com.jme3.texture.Texture2D; 48 import com.jme3.ui.Picture; 49 50 /** 51 * BasicShadowRenderer uses standard shadow mapping with one map 52 * it's useful to render shadows in a small scene, but edges might look a bit jagged. 53 * 54 * @author Kirill Vainer 55 */ 56 public class BasicShadowRenderer implements SceneProcessor { 57 58 private RenderManager renderManager; 59 private ViewPort viewPort; 60 private FrameBuffer shadowFB; 61 private Texture2D shadowMap; 62 private Camera shadowCam; 63 private Material preshadowMat; 64 private Material postshadowMat; 65 private Picture dispPic = new Picture("Picture"); 66 private boolean noOccluders = false; 67 private Vector3f[] points = new Vector3f[8]; 68 private Vector3f direction = new Vector3f(); 69 70 /** 71 * Creates a BasicShadowRenderer 72 * @param manager the asset manager 73 * @param size the size of the shadow map (the map is square) 74 */ 75 public BasicShadowRenderer(AssetManager manager, int size) { 76 shadowFB = new FrameBuffer(size, size, 1); 77 shadowMap = new Texture2D(size, size, Format.Depth); 78 shadowFB.setDepthTexture(shadowMap); 79 shadowCam = new Camera(size, size); 80 81 preshadowMat = new Material(manager, "Common/MatDefs/Shadow/PreShadow.j3md"); 82 postshadowMat = new Material(manager, "Common/MatDefs/Shadow/PostShadow.j3md"); 83 postshadowMat.setTexture("ShadowMap", shadowMap); 84 85 dispPic.setTexture(manager, shadowMap, false); 86 87 for (int i = 0; i < points.length; i++) { 88 points[i] = new Vector3f(); 89 } 90 } 91 92 public void initialize(RenderManager rm, ViewPort vp) { 93 renderManager = rm; 94 viewPort = vp; 95 96 reshape(vp, vp.getCamera().getWidth(), vp.getCamera().getHeight()); 97 } 98 99 public boolean isInitialized() { 100 return viewPort != null; 101 } 102 103 /** 104 * returns the light direction used for this processor 105 * @return 106 */ 107 public Vector3f getDirection() { 108 return direction; 109 } 110 111 /** 112 * sets the light direction to use to computs shadows 113 * @param direction 114 */ 115 public void setDirection(Vector3f direction) { 116 this.direction.set(direction).normalizeLocal(); 117 } 118 119 /** 120 * debug only 121 * @return 122 */ 123 public Vector3f[] getPoints() { 124 return points; 125 } 126 127 /** 128 * debug only 129 * returns the shadow camera 130 * @return 131 */ 132 public Camera getShadowCamera() { 133 return shadowCam; 134 } 135 136 public void postQueue(RenderQueue rq) { 137 GeometryList occluders = rq.getShadowQueueContent(ShadowMode.Cast); 138 if (occluders.size() == 0) { 139 noOccluders = true; 140 return; 141 } else { 142 noOccluders = false; 143 } 144 145 GeometryList receivers = rq.getShadowQueueContent(ShadowMode.Receive); 146 147 // update frustum points based on current camera 148 Camera viewCam = viewPort.getCamera(); 149 ShadowUtil.updateFrustumPoints(viewCam, 150 viewCam.getFrustumNear(), 151 viewCam.getFrustumFar(), 152 1.0f, 153 points); 154 155 Vector3f frustaCenter = new Vector3f(); 156 for (Vector3f point : points) { 157 frustaCenter.addLocal(point); 158 } 159 frustaCenter.multLocal(1f / 8f); 160 161 // update light direction 162 shadowCam.setProjectionMatrix(null); 163 shadowCam.setParallelProjection(true); 164 // shadowCam.setFrustumPerspective(45, 1, 1, 20); 165 166 shadowCam.lookAtDirection(direction, Vector3f.UNIT_Y); 167 shadowCam.update(); 168 shadowCam.setLocation(frustaCenter); 169 shadowCam.update(); 170 shadowCam.updateViewProjection(); 171 172 // render shadow casters to shadow map 173 ShadowUtil.updateShadowCamera(occluders, receivers, shadowCam, points); 174 175 Renderer r = renderManager.getRenderer(); 176 renderManager.setCamera(shadowCam, false); 177 renderManager.setForcedMaterial(preshadowMat); 178 179 r.setFrameBuffer(shadowFB); 180 r.clearBuffers(false, true, false); 181 viewPort.getQueue().renderShadowQueue(ShadowMode.Cast, renderManager, shadowCam, true); 182 r.setFrameBuffer(viewPort.getOutputFrameBuffer()); 183 184 renderManager.setForcedMaterial(null); 185 renderManager.setCamera(viewCam, false); 186 } 187 188 /** 189 * debug only 190 * @return 191 */ 192 public Picture getDisplayPicture() { 193 return dispPic; 194 } 195 196 public void postFrame(FrameBuffer out) { 197 if (!noOccluders) { 198 postshadowMat.setMatrix4("LightViewProjectionMatrix", shadowCam.getViewProjectionMatrix()); 199 renderManager.setForcedMaterial(postshadowMat); 200 viewPort.getQueue().renderShadowQueue(ShadowMode.Receive, renderManager, viewPort.getCamera(), true); 201 renderManager.setForcedMaterial(null); 202 } 203 } 204 205 public void preFrame(float tpf) { 206 } 207 208 public void cleanup() { 209 } 210 211 public void reshape(ViewPort vp, int w, int h) { 212 dispPic.setPosition(w / 20f, h / 20f); 213 dispPic.setWidth(w / 5f); 214 dispPic.setHeight(h / 5f); 215 } 216 } 217