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.post; 33 34 import com.jme3.asset.AssetManager; 35 import com.jme3.export.*; 36 import com.jme3.material.Material; 37 import com.jme3.renderer.Caps; 38 import com.jme3.renderer.RenderManager; 39 import com.jme3.renderer.Renderer; 40 import com.jme3.renderer.ViewPort; 41 import com.jme3.texture.FrameBuffer; 42 import com.jme3.texture.Image.Format; 43 import com.jme3.texture.Texture; 44 import com.jme3.texture.Texture2D; 45 import java.io.IOException; 46 import java.util.Collection; 47 import java.util.Iterator; 48 import java.util.List; 49 50 /** 51 * Filters are 2D effects applied to the rendered scene.<br> 52 * The filter is fed with the rendered scene image rendered in an offscreen frame buffer.<br> 53 * This texture is applied on a fullscreen quad, with a special material.<br> 54 * This material uses a shader that aplly the desired effect to the scene texture.<br> 55 * <br> 56 * This class is abstract, any Filter must extend it.<br> 57 * Any filter holds a frameBuffer and a texture<br> 58 * The getMaterial must return a Material that use a GLSL shader immplementing the desired effect<br> 59 * 60 * @author Rémy Bouquet aka Nehon 61 */ 62 public abstract class Filter implements Savable { 63 64 65 private String name; 66 protected Pass defaultPass; 67 protected List<Pass> postRenderPasses; 68 protected Material material; 69 protected boolean enabled = true; 70 protected FilterPostProcessor processor; 71 Filter(String name)72 public Filter(String name) { 73 this.name = name; 74 } 75 76 /** 77 * Inner class Pass 78 * Pass are like filters in filters. 79 * Some filters will need multiple passes before the final render 80 */ 81 public class Pass { 82 83 protected FrameBuffer renderFrameBuffer; 84 protected Texture2D renderedTexture; 85 protected Texture2D depthTexture; 86 protected Material passMaterial; 87 88 /** 89 * init the pass called internally 90 * @param renderer 91 * @param width 92 * @param height 93 * @param textureFormat 94 * @param depthBufferFormat 95 * @param numSamples 96 */ init(Renderer renderer, int width, int height, Format textureFormat, Format depthBufferFormat, int numSamples, boolean renderDepth)97 public void init(Renderer renderer, int width, int height, Format textureFormat, Format depthBufferFormat, int numSamples, boolean renderDepth) { 98 Collection<Caps> caps = renderer.getCaps(); 99 if (numSamples > 1 && caps.contains(Caps.FrameBufferMultisample) && caps.contains(Caps.OpenGL31)) { 100 renderFrameBuffer = new FrameBuffer(width, height, numSamples); 101 renderedTexture = new Texture2D(width, height, numSamples, textureFormat); 102 renderFrameBuffer.setDepthBuffer(depthBufferFormat); 103 if (renderDepth) { 104 depthTexture = new Texture2D(width, height, numSamples, depthBufferFormat); 105 renderFrameBuffer.setDepthTexture(depthTexture); 106 } 107 } else { 108 renderFrameBuffer = new FrameBuffer(width, height, 1); 109 renderedTexture = new Texture2D(width, height, textureFormat); 110 renderFrameBuffer.setDepthBuffer(depthBufferFormat); 111 if (renderDepth) { 112 depthTexture = new Texture2D(width, height, depthBufferFormat); 113 renderFrameBuffer.setDepthTexture(depthTexture); 114 } 115 } 116 117 renderFrameBuffer.setColorTexture(renderedTexture); 118 119 120 } 121 122 /** 123 * init the pass called internally 124 * @param renderer 125 * @param width 126 * @param height 127 * @param textureFormat 128 * @param depthBufferFormat 129 */ init(Renderer renderer, int width, int height, Format textureFormat, Format depthBufferFormat)130 public void init(Renderer renderer, int width, int height, Format textureFormat, Format depthBufferFormat) { 131 init(renderer, width, height, textureFormat, depthBufferFormat, 1); 132 } 133 init(Renderer renderer, int width, int height, Format textureFormat, Format depthBufferFormat, int numSamples)134 public void init(Renderer renderer, int width, int height, Format textureFormat, Format depthBufferFormat, int numSamples) { 135 init(renderer, width, height, textureFormat, depthBufferFormat, numSamples, false); 136 } 137 138 /** 139 * init the pass called internally 140 * @param renderer 141 * @param width 142 * @param height 143 * @param textureFormat 144 * @param depthBufferFormat 145 * @param numSample 146 * @param material 147 */ init(Renderer renderer, int width, int height, Format textureFormat, Format depthBufferFormat, int numSample, Material material)148 public void init(Renderer renderer, int width, int height, Format textureFormat, Format depthBufferFormat, int numSample, Material material) { 149 init(renderer, width, height, textureFormat, depthBufferFormat, numSample); 150 passMaterial = material; 151 } 152 requiresSceneAsTexture()153 public boolean requiresSceneAsTexture() { 154 return false; 155 } 156 requiresDepthAsTexture()157 public boolean requiresDepthAsTexture() { 158 return false; 159 } 160 beforeRender()161 public void beforeRender() { 162 } 163 getRenderFrameBuffer()164 public FrameBuffer getRenderFrameBuffer() { 165 return renderFrameBuffer; 166 } 167 setRenderFrameBuffer(FrameBuffer renderFrameBuffer)168 public void setRenderFrameBuffer(FrameBuffer renderFrameBuffer) { 169 this.renderFrameBuffer = renderFrameBuffer; 170 } 171 getDepthTexture()172 public Texture2D getDepthTexture() { 173 return depthTexture; 174 } 175 getRenderedTexture()176 public Texture2D getRenderedTexture() { 177 return renderedTexture; 178 } 179 setRenderedTexture(Texture2D renderedTexture)180 public void setRenderedTexture(Texture2D renderedTexture) { 181 this.renderedTexture = renderedTexture; 182 } 183 getPassMaterial()184 public Material getPassMaterial() { 185 return passMaterial; 186 } 187 setPassMaterial(Material passMaterial)188 public void setPassMaterial(Material passMaterial) { 189 this.passMaterial = passMaterial; 190 } 191 cleanup(Renderer r)192 public void cleanup(Renderer r) { 193 } 194 } 195 196 /** 197 * returns the default pass texture format 198 * @return 199 */ getDefaultPassTextureFormat()200 protected Format getDefaultPassTextureFormat() { 201 return Format.RGBA8; 202 } 203 204 /** 205 * returns the default pass depth format 206 * @return 207 */ getDefaultPassDepthFormat()208 protected Format getDefaultPassDepthFormat() { 209 return Format.Depth; 210 } 211 212 /** 213 * contruct a Filter 214 */ Filter()215 protected Filter() { 216 this("filter"); 217 } 218 219 /** 220 * 221 * initialize this filter 222 * use InitFilter for overriding filter initialization 223 * @param manager the assetManager 224 * @param renderManager the renderManager 225 * @param vp the viewport 226 * @param w the width 227 * @param h the height 228 */ init(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h)229 protected final void init(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) { 230 // cleanup(renderManager.getRenderer()); 231 defaultPass = new Pass(); 232 defaultPass.init(renderManager.getRenderer(), w, h, getDefaultPassTextureFormat(), getDefaultPassDepthFormat()); 233 initFilter(manager, renderManager, vp, w, h); 234 } 235 236 /** 237 * cleanup this filter 238 * @param r 239 */ cleanup(Renderer r)240 protected final void cleanup(Renderer r) { 241 processor = null; 242 if (defaultPass != null) { 243 defaultPass.cleanup(r); 244 } 245 if (postRenderPasses != null) { 246 for (Iterator<Pass> it = postRenderPasses.iterator(); it.hasNext();) { 247 Pass pass = it.next(); 248 pass.cleanup(r); 249 } 250 } 251 cleanUpFilter(r); 252 } 253 254 /** 255 * Initialization of sub classes filters 256 * This method is called once when the filter is added to the FilterPostProcessor 257 * It should contain Material initializations and extra passes initialization 258 * @param manager the assetManager 259 * @param renderManager the renderManager 260 * @param vp the viewPort where this filter is rendered 261 * @param w the width of the filter 262 * @param h the height of the filter 263 */ initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h)264 protected abstract void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h); 265 266 /** 267 * override this method if you have some cleanup to do 268 * @param r the renderer 269 */ cleanUpFilter(Renderer r)270 protected void cleanUpFilter(Renderer r) { 271 } 272 273 /** 274 * Must return the material used for this filter. 275 * this method is called every frame. 276 * 277 * @return the material used for this filter. 278 */ getMaterial()279 protected abstract Material getMaterial(); 280 281 /** 282 * Override if you want to do something special with the depth texture; 283 * @param depthTexture 284 */ setDepthTexture(Texture depthTexture)285 protected void setDepthTexture(Texture depthTexture){ 286 getMaterial().setTexture("DepthTexture", depthTexture); 287 } 288 289 /** 290 * Override this method if you want to make a pre pass, before the actual rendering of the frame 291 * @param renderManager 292 * @param viewPort 293 */ postQueue(RenderManager renderManager, ViewPort viewPort)294 protected void postQueue(RenderManager renderManager, ViewPort viewPort) { 295 } 296 297 /** 298 * Override this method if you want to modify parameters according to tpf before the rendering of the frame. 299 * This is usefull for animated filters 300 * Also it can be the place to render pre passes 301 * @param tpf the time used to render the previous frame 302 */ preFrame(float tpf)303 protected void preFrame(float tpf) { 304 } 305 306 /** 307 * Override this method if you want to make a pass just after the frame has been rendered and just before the filter rendering 308 * @param renderManager 309 * @param viewPort 310 * @param prevFilterBuffer 311 * @param sceneBuffer 312 */ postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer)313 protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) { 314 } 315 316 /** 317 * Override this method if you want to save extra properties when the filter is saved else only basic properties of the filter will be saved 318 * This method should always begin by super.write(ex); 319 * @param ex 320 * @throws IOException 321 */ write(JmeExporter ex)322 public void write(JmeExporter ex) throws IOException { 323 OutputCapsule oc = ex.getCapsule(this); 324 oc.write(name, "name", ""); 325 oc.write(enabled, "enabled", true); 326 } 327 328 /** 329 * Override this method if you want to load extra properties when the filter 330 * is loaded else only basic properties of the filter will be loaded 331 * This method should always begin by super.read(im); 332 */ read(JmeImporter im)333 public void read(JmeImporter im) throws IOException { 334 InputCapsule ic = im.getCapsule(this); 335 name = ic.readString("name", ""); 336 enabled = ic.readBoolean("enabled", true); 337 } 338 339 /** 340 * returns the name of the filter 341 * @return 342 */ getName()343 public String getName() { 344 return name; 345 } 346 347 /** 348 * Sets the name of the filter 349 * @param name 350 */ setName(String name)351 public void setName(String name) { 352 this.name = name; 353 } 354 355 /** 356 * returns the default pass frame buffer 357 * @return 358 */ getRenderFrameBuffer()359 protected FrameBuffer getRenderFrameBuffer() { 360 return defaultPass.renderFrameBuffer; 361 } 362 363 /** 364 * sets the default pas frame buffer 365 * @param renderFrameBuffer 366 */ setRenderFrameBuffer(FrameBuffer renderFrameBuffer)367 protected void setRenderFrameBuffer(FrameBuffer renderFrameBuffer) { 368 this.defaultPass.renderFrameBuffer = renderFrameBuffer; 369 } 370 371 /** 372 * returns the rendered texture of this filter 373 * @return 374 */ getRenderedTexture()375 protected Texture2D getRenderedTexture() { 376 return defaultPass.renderedTexture; 377 } 378 379 /** 380 * sets the rendered texture of this filter 381 * @param renderedTexture 382 */ setRenderedTexture(Texture2D renderedTexture)383 protected void setRenderedTexture(Texture2D renderedTexture) { 384 this.defaultPass.renderedTexture = renderedTexture; 385 } 386 387 /** 388 * Override this method and return true if your Filter needs the depth texture 389 * 390 * @return true if your Filter need the depth texture 391 */ isRequiresDepthTexture()392 protected boolean isRequiresDepthTexture() { 393 return false; 394 } 395 396 /** 397 * Override this method and return false if your Filter does not need the scene texture 398 * 399 * @return false if your Filter does not need the scene texture 400 */ isRequiresSceneTexture()401 protected boolean isRequiresSceneTexture() { 402 return true; 403 } 404 405 /** 406 * returns the list of the postRender passes 407 * @return 408 */ getPostRenderPasses()409 protected List<Pass> getPostRenderPasses() { 410 return postRenderPasses; 411 } 412 413 /** 414 * Enable or disable this filter 415 * @param enabled true to enable 416 */ setEnabled(boolean enabled)417 public void setEnabled(boolean enabled) { 418 if (processor != null) { 419 processor.setFilterState(this, enabled); 420 } else { 421 this.enabled = enabled; 422 } 423 } 424 425 /** 426 * returns ttrue if the filter is enabled 427 * @return enabled 428 */ isEnabled()429 public boolean isEnabled() { 430 return enabled; 431 } 432 433 /** 434 * sets a reference to the FilterPostProcessor ti which this filter is attached 435 * @param proc 436 */ setProcessor(FilterPostProcessor proc)437 protected void setProcessor(FilterPostProcessor proc) { 438 processor = proc; 439 } 440 } 441