Home | History | Annotate | Download | only in post
      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 Rmy 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 
     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          */
     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          */
    130         public void init(Renderer renderer, int width, int height, Format textureFormat, Format depthBufferFormat) {
    131             init(renderer, width, height, textureFormat, depthBufferFormat, 1);
    132         }
    133 
    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          */
    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 
    153         public boolean requiresSceneAsTexture() {
    154             return false;
    155         }
    156 
    157         public boolean requiresDepthAsTexture() {
    158             return false;
    159         }
    160 
    161         public void beforeRender() {
    162         }
    163 
    164         public FrameBuffer getRenderFrameBuffer() {
    165             return renderFrameBuffer;
    166         }
    167 
    168         public void setRenderFrameBuffer(FrameBuffer renderFrameBuffer) {
    169             this.renderFrameBuffer = renderFrameBuffer;
    170         }
    171 
    172         public Texture2D getDepthTexture() {
    173             return depthTexture;
    174         }
    175 
    176         public Texture2D getRenderedTexture() {
    177             return renderedTexture;
    178         }
    179 
    180         public void setRenderedTexture(Texture2D renderedTexture) {
    181             this.renderedTexture = renderedTexture;
    182         }
    183 
    184         public Material getPassMaterial() {
    185             return passMaterial;
    186         }
    187 
    188         public void setPassMaterial(Material passMaterial) {
    189             this.passMaterial = passMaterial;
    190         }
    191 
    192         public void cleanup(Renderer r) {
    193         }
    194     }
    195 
    196     /**
    197      * returns the default pass texture format
    198      * @return
    199      */
    200     protected Format getDefaultPassTextureFormat() {
    201         return Format.RGBA8;
    202     }
    203 
    204     /**
    205      * returns the default pass depth format
    206      * @return
    207      */
    208     protected Format getDefaultPassDepthFormat() {
    209         return Format.Depth;
    210     }
    211 
    212     /**
    213      * contruct a Filter
    214      */
    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      */
    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      */
    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      */
    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      */
    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      */
    279     protected abstract Material getMaterial();
    280 
    281     /**
    282      * Override if you want to do something special with the depth texture;
    283      * @param depthTexture
    284      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    343     public String getName() {
    344         return name;
    345     }
    346 
    347     /**
    348      * Sets the name of the filter
    349      * @param name
    350      */
    351     public void setName(String name) {
    352         this.name = name;
    353     }
    354 
    355     /**
    356      * returns the default pass frame buffer
    357      * @return
    358      */
    359     protected FrameBuffer getRenderFrameBuffer() {
    360         return defaultPass.renderFrameBuffer;
    361     }
    362 
    363     /**
    364      * sets the default pas frame buffer
    365      * @param renderFrameBuffer
    366      */
    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      */
    375     protected Texture2D getRenderedTexture() {
    376         return defaultPass.renderedTexture;
    377     }
    378 
    379     /**
    380      * sets the rendered texture of this filter
    381      * @param renderedTexture
    382      */
    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      */
    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      */
    401     protected boolean isRequiresSceneTexture() {
    402         return true;
    403     }
    404 
    405     /**
    406      * returns the list of the postRender passes
    407      * @return
    408      */
    409     protected List<Pass> getPostRenderPasses() {
    410         return postRenderPasses;
    411     }
    412 
    413     /**
    414      * Enable or disable this filter
    415      * @param enabled true to enable
    416      */
    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      */
    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      */
    437     protected void setProcessor(FilterPostProcessor proc) {
    438         processor = proc;
    439     }
    440 }
    441