Home | History | Annotate | Download | only in material
      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.material;
     33 
     34 import com.jme3.asset.AssetManager;
     35 import com.jme3.export.*;
     36 import com.jme3.shader.*;
     37 import java.io.IOException;
     38 import java.util.ArrayList;
     39 import java.util.Collection;
     40 import java.util.List;
     41 import java.util.logging.Logger;
     42 
     43 /**
     44  * Represents a technique instance.
     45  */
     46 public class Technique implements Savable {
     47 
     48     private static final Logger logger = Logger.getLogger(Technique.class.getName());
     49     private TechniqueDef def;
     50     private Material owner;
     51     private ArrayList<Uniform> worldBindUniforms;
     52     private DefineList defines;
     53     private Shader shader;
     54     private boolean needReload = true;
     55 
     56     /**
     57      * Creates a new technique instance that implements the given
     58      * technique definition.
     59      *
     60      * @param owner The material that will own this technique
     61      * @param def The technique definition being implemented.
     62      */
     63     public Technique(Material owner, TechniqueDef def) {
     64         this.owner = owner;
     65         this.def = def;
     66         if (def.isUsingShaders()) {
     67             this.worldBindUniforms = new ArrayList<Uniform>();
     68             this.defines = new DefineList();
     69         }
     70     }
     71 
     72     /**
     73      * Serialization only. Do not use.
     74      */
     75     public Technique() {
     76     }
     77 
     78     /**
     79      * Returns the technique definition that is implemented by this technique
     80      * instance.
     81      *
     82      * @return the technique definition that is implemented by this technique
     83      * instance.
     84      */
     85     public TechniqueDef getDef() {
     86         return def;
     87     }
     88 
     89     /**
     90      * Returns the shader currently used by this technique instance.
     91      * <p>
     92      * Shaders are typically loaded dynamically when the technique is first
     93      * used, therefore, this variable will most likely be null most of the time.
     94      *
     95      * @return the shader currently used by this technique instance.
     96      */
     97     public Shader getShader() {
     98         return shader;
     99     }
    100 
    101     /**
    102      * Returns a list of uniforms that implements the world parameters
    103      * that were requested by the material definition.
    104      *
    105      * @return a list of uniforms implementing the world parameters.
    106      */
    107     public List<Uniform> getWorldBindUniforms() {
    108         return worldBindUniforms;
    109     }
    110 
    111     /**
    112      * Called by the material to tell the technique a parameter was modified
    113      */
    114     void notifySetParam(String paramName, VarType type, Object value) {
    115         String defineName = def.getShaderParamDefine(paramName);
    116         if (defineName != null) {
    117             needReload = defines.set(defineName, type, value);
    118         }
    119         if (shader != null) {
    120             updateUniformParam(paramName, type, value);
    121         }
    122     }
    123 
    124     /**
    125      * Called by the material to tell the technique a parameter was cleared
    126      */
    127     void notifyClearParam(String paramName) {
    128         String defineName = def.getShaderParamDefine(paramName);
    129         if (defineName != null) {
    130             needReload = defines.remove(defineName);
    131         }
    132         if (shader != null) {
    133             if (!paramName.startsWith("m_")) {
    134                 paramName = "m_" + paramName;
    135             }
    136             shader.removeUniform(paramName);
    137         }
    138     }
    139 
    140     void updateUniformParam(String paramName, VarType type, Object value, boolean ifNotOwner) {
    141         Uniform u = shader.getUniform(paramName);
    142 
    143 //        if (ifNotOwner && u.getLastChanger() == owner)
    144 //            return;
    145 
    146         switch (type) {
    147             case Texture2D: // fall intentional
    148             case Texture3D:
    149             case TextureArray:
    150             case TextureCubeMap:
    151             case Int:
    152                 u.setValue(VarType.Int, value);
    153                 break;
    154             default:
    155                 u.setValue(type, value);
    156                 break;
    157         }
    158 //        u.setLastChanger(owner);
    159     }
    160 
    161     void updateUniformParam(String paramName, VarType type, Object value) {
    162         updateUniformParam(paramName, type, value, false);
    163     }
    164 
    165     /**
    166      * Returns true if the technique must be reloaded.
    167      * <p>
    168      * If a technique needs to reload, then the {@link Material} should
    169      * call {@link #makeCurrent(com.jme3.asset.AssetManager) } on this
    170      * technique.
    171      *
    172      * @return true if the technique must be reloaded.
    173      */
    174     public boolean isNeedReload() {
    175         return needReload;
    176     }
    177 
    178     /**
    179      * Prepares the technique for use by loading the shader and setting
    180      * the proper defines based on material parameters.
    181      *
    182      * @param assetManager The asset manager to use for loading shaders.
    183      */
    184     public void makeCurrent(AssetManager assetManager) {
    185         // check if reload is needed..
    186         if (def.isUsingShaders()) {
    187             DefineList newDefines = new DefineList();
    188             Collection<MatParam> params = owner.getParams();
    189             for (MatParam param : params) {
    190                 String defineName = def.getShaderParamDefine(param.getName());
    191                 if (defineName != null) {
    192                     newDefines.set(defineName, param.getVarType(), param.getValue());
    193                 }
    194             }
    195 
    196             if (!needReload && defines.getCompiled().equals(newDefines.getCompiled())) {
    197                 newDefines = null;
    198                 // defines have not been changed..
    199             } else {
    200                 defines.clear();
    201                 defines.addFrom(newDefines);
    202                 // defines changed, recompile needed
    203                 loadShader(assetManager);
    204             }
    205         }
    206     }
    207 
    208     private void loadShader(AssetManager manager) {
    209         // recompute define list
    210         DefineList allDefines = new DefineList();
    211         allDefines.addFrom(def.getShaderPresetDefines());
    212         allDefines.addFrom(defines);
    213 
    214         ShaderKey key = new ShaderKey(def.getVertexShaderName(),
    215                 def.getFragmentShaderName(),
    216                 allDefines,
    217                 def.getShaderLanguage());
    218         shader = manager.loadShader(key);
    219         if (shader == null) {
    220             logger.warning("Failed to reload shader!");
    221             return;
    222         }
    223 
    224         // refresh the uniform links
    225         //owner.updateUniformLinks();
    226 
    227         // register the world bound uniforms
    228         worldBindUniforms.clear();
    229         if (def.getWorldBindings() != null) {
    230            for (UniformBinding binding : def.getWorldBindings()) {
    231                Uniform uniform = shader.getUniform("g_" + binding.name());
    232                uniform.setBinding(binding);
    233                if (uniform != null) {
    234                    worldBindUniforms.add(uniform);
    235                }
    236            }
    237         }
    238 
    239         needReload = false;
    240     }
    241 
    242     public void write(JmeExporter ex) throws IOException {
    243         OutputCapsule oc = ex.getCapsule(this);
    244         oc.write(def, "def", null);
    245         // TODO:
    246         // oc.write(owner, "owner", null);
    247         oc.writeSavableArrayList(worldBindUniforms, "worldBindUniforms", null);
    248         oc.write(defines, "defines", null);
    249         oc.write(shader, "shader", null);
    250     }
    251 
    252     public void read(JmeImporter im) throws IOException {
    253         InputCapsule ic = im.getCapsule(this);
    254         def = (TechniqueDef) ic.readSavable("def", null);
    255         worldBindUniforms = ic.readSavableArrayList("worldBindUniforms", null);
    256         defines = (DefineList) ic.readSavable("defines", null);
    257         shader = (Shader) ic.readSavable("shader", null);
    258         //if (shader != null)
    259         //    owner.updateUniformLinks();
    260     }
    261 }
    262