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
33package com.jme3.material;
34
35import com.jme3.export.*;
36import com.jme3.renderer.Caps;
37import com.jme3.renderer.Renderer;
38import com.jme3.shader.DefineList;
39import com.jme3.shader.UniformBinding;
40import com.jme3.shader.VarType;
41import java.io.IOException;
42import java.util.ArrayList;
43import java.util.EnumSet;
44import java.util.HashMap;
45import java.util.List;
46
47/**
48 * Describes a technique definition.
49 *
50 * @author Kirill Vainer
51 */
52public class TechniqueDef implements Savable {
53
54    /**
55     * Describes light rendering mode.
56     */
57    public enum LightMode {
58        /**
59         * Disable light-based rendering
60         */
61        Disable,
62
63        /**
64         * Enable light rendering by using a single pass.
65         * <p>
66         * An array of light positions and light colors is passed to the shader
67         * containing the world light list for the geometry being rendered.
68         */
69        SinglePass,
70
71        /**
72         * Enable light rendering by using multi-pass rendering.
73         * <p>
74         * The geometry will be rendered once for each light. Each time the
75         * light position and light color uniforms are updated to contain
76         * the values for the current light. The ambient light color uniform
77         * is only set to the ambient light color on the first pass, future
78         * passes have it set to black.
79         */
80        MultiPass,
81
82        /**
83         * Enable light rendering by using the
84         * {@link Renderer#setLighting(com.jme3.light.LightList) renderer's setLighting}
85         * method.
86         * <p>
87         * The specific details of rendering the lighting is up to the
88         * renderer implementation.
89         */
90        FixedPipeline,
91    }
92
93    public enum ShadowMode {
94        Disable,
95        InPass,
96        PostPass,
97    }
98
99    private EnumSet<Caps> requiredCaps = EnumSet.noneOf(Caps.class);
100    private String name;
101
102    private String vertName;
103    private String fragName;
104    private String shaderLang;
105    private DefineList presetDefines;
106    private boolean usesShaders;
107
108    private RenderState renderState;
109    private LightMode lightMode   = LightMode.Disable;
110    private ShadowMode shadowMode = ShadowMode.Disable;
111
112    private HashMap<String, String> defineParams;
113    private ArrayList<UniformBinding> worldBinds;
114
115    /**
116     * Creates a new technique definition.
117     * <p>
118     * Used internally by the J3M/J3MD loader.
119     *
120     * @param name The name of the technique, should be set to <code>null</code>
121     * for default techniques.
122     */
123    public TechniqueDef(String name){
124        this.name = name == null ? "Default" : name;
125    }
126
127    /**
128     * Serialization only. Do not use.
129     */
130    public TechniqueDef(){
131    }
132
133    /**
134     * Returns the name of this technique as specified in the J3MD file.
135     * Default techniques have the name "Default".
136     *
137     * @return the name of this technique
138     */
139    public String getName(){
140        return name;
141    }
142
143    /**
144     * Returns the light mode.
145     * @return the light mode.
146     * @see LightMode
147     */
148    public LightMode getLightMode() {
149        return lightMode;
150    }
151
152    /**
153     * Set the light mode
154     *
155     * @param lightMode the light mode
156     *
157     * @see LightMode
158     */
159    public void setLightMode(LightMode lightMode) {
160        this.lightMode = lightMode;
161    }
162
163    /**
164     * Returns the shadow mode.
165     * @return the shadow mode.
166     */
167    public ShadowMode getShadowMode() {
168        return shadowMode;
169    }
170
171    /**
172     * Set the shadow mode.
173     *
174     * @param shadowMode the shadow mode.
175     *
176     * @see ShadowMode
177     */
178    public void setShadowMode(ShadowMode shadowMode) {
179        this.shadowMode = shadowMode;
180    }
181
182    /**
183     * Returns the render state that this technique is using
184     * @return the render state that this technique is using
185     * @see #setRenderState(com.jme3.material.RenderState)
186     */
187    public RenderState getRenderState() {
188        return renderState;
189    }
190
191    /**
192     * Sets the render state that this technique is using.
193     *
194     * @param renderState the render state that this technique is using.
195     *
196     * @see RenderState
197     */
198    public void setRenderState(RenderState renderState) {
199        this.renderState = renderState;
200    }
201
202    /**
203     * Returns true if this technique uses shaders, false otherwise.
204     *
205     * @return true if this technique uses shaders, false otherwise.
206     *
207     * @see #setShaderFile(java.lang.String, java.lang.String, java.lang.String)
208     */
209    public boolean isUsingShaders(){
210        return usesShaders;
211    }
212
213    /**
214     * Gets the {@link Caps renderer capabilities} that are required
215     * by this technique.
216     *
217     * @return the required renderer capabilities
218     */
219    public EnumSet<Caps> getRequiredCaps() {
220        return requiredCaps;
221    }
222
223    /**
224     * Sets the shaders that this technique definition will use.
225     *
226     * @param vertexShader The name of the vertex shader
227     * @param fragmentShader The name of the fragment shader
228     * @param shaderLanguage The shader language
229     */
230    public void setShaderFile(String vertexShader, String fragmentShader, String shaderLanguage){
231        this.vertName = vertexShader;
232        this.fragName = fragmentShader;
233        this.shaderLang = shaderLanguage;
234
235        Caps langCap = Caps.valueOf(shaderLanguage);
236        requiredCaps.add(langCap);
237
238        usesShaders = true;
239    }
240
241    /**
242     * Returns the define name which the given material parameter influences.
243     *
244     * @param paramName The parameter name to look up
245     * @return The define name
246     *
247     * @see #addShaderParamDefine(java.lang.String, java.lang.String)
248     */
249    public String getShaderParamDefine(String paramName){
250        if (defineParams == null)
251            return null;
252
253        return defineParams.get(paramName);
254    }
255
256    /**
257     * Adds a define linked to a material parameter.
258     * <p>
259     * Any time the material parameter on the parent material is altered,
260     * the appropriate define on the technique will be modified as well.
261     * See the method
262     * {@link DefineList#set(java.lang.String, com.jme3.shader.VarType, java.lang.Object) }
263     * on the exact details of how the material parameter changes the define.
264     *
265     * @param paramName The name of the material parameter to link to.
266     * @param defineName The name of the define parameter, e.g. USE_LIGHTING
267     */
268    public void addShaderParamDefine(String paramName, String defineName){
269        if (defineParams == null)
270            defineParams = new HashMap<String, String>();
271
272        defineParams.put(paramName, defineName);
273    }
274
275    /**
276     * Returns the {@link DefineList} for the preset defines.
277     *
278     * @return the {@link DefineList} for the preset defines.
279     *
280     * @see #addShaderPresetDefine(java.lang.String, com.jme3.shader.VarType, java.lang.Object)
281     */
282    public DefineList getShaderPresetDefines() {
283        return presetDefines;
284    }
285
286    /**
287     * Adds a preset define.
288     * <p>
289     * Preset defines do not depend upon any parameters to be activated,
290     * they are always passed to the shader as long as this technique is used.
291     *
292     * @param defineName The name of the define parameter, e.g. USE_LIGHTING
293     * @param type The type of the define. See
294     * {@link DefineList#set(java.lang.String, com.jme3.shader.VarType, java.lang.Object) }
295     * to see why it matters.
296     *
297     * @param value The value of the define
298     */
299    public void addShaderPresetDefine(String defineName, VarType type, Object value){
300        if (presetDefines == null)
301            presetDefines = new DefineList();
302
303        presetDefines.set(defineName, type, value);
304    }
305
306    /**
307     * Returns the name of the fragment shader used by the technique, or null
308     * if no fragment shader is specified.
309     *
310     * @return the name of the fragment shader to be used.
311     */
312    public String getFragmentShaderName() {
313        return fragName;
314    }
315
316
317    /**
318     * Returns the name of the vertex shader used by the technique, or null
319     * if no vertex shader is specified.
320     *
321     * @return the name of the vertex shader to be used.
322     */
323    public String getVertexShaderName() {
324        return vertName;
325    }
326
327    /**
328     * Returns the shader language of the shaders used in this technique.
329     *
330     * @return the shader language of the shaders used in this technique.
331     */
332    public String getShaderLanguage() {
333        return shaderLang;
334    }
335
336    /**
337     * Adds a new world parameter by the given name.
338     *
339     * @param name The world parameter to add.
340     * @return True if the world parameter name was found and added
341     * to the list of world parameters, false otherwise.
342     */
343    public boolean addWorldParam(String name) {
344        if (worldBinds == null){
345            worldBinds = new ArrayList<UniformBinding>();
346        }
347
348        try {
349            worldBinds.add( UniformBinding.valueOf(name) );
350            return true;
351        } catch (IllegalArgumentException ex){
352            return false;
353        }
354    }
355
356    /**
357     * Returns a list of world parameters that are used by this
358     * technique definition.
359     *
360     * @return The list of world parameters
361     */
362    public List<UniformBinding> getWorldBindings() {
363        return worldBinds;
364    }
365
366    public void write(JmeExporter ex) throws IOException{
367        OutputCapsule oc = ex.getCapsule(this);
368        oc.write(name, "name", null);
369        oc.write(vertName, "vertName", null);
370        oc.write(fragName, "fragName", null);
371        oc.write(shaderLang, "shaderLang", null);
372        oc.write(presetDefines, "presetDefines", null);
373        oc.write(lightMode, "lightMode", LightMode.Disable);
374        oc.write(shadowMode, "shadowMode", ShadowMode.Disable);
375        oc.write(renderState, "renderState", null);
376        oc.write(usesShaders, "usesShaders", false);
377        // TODO: Finish this when Map<String, String> export is available
378//        oc.write(defineParams, "defineParams", null);
379        // TODO: Finish this when List<Enum> export is available
380//        oc.write(worldBinds, "worldBinds", null);
381    }
382
383    public void read(JmeImporter im) throws IOException{
384        InputCapsule ic = im.getCapsule(this);
385        name = ic.readString("name", null);
386        vertName = ic.readString("vertName", null);
387        fragName = ic.readString("fragName", null);
388        shaderLang = ic.readString("shaderLang", null);
389        presetDefines = (DefineList) ic.readSavable("presetDefines", null);
390        lightMode = ic.readEnum("lightMode", LightMode.class, LightMode.Disable);
391        shadowMode = ic.readEnum("shadowMode", ShadowMode.class, ShadowMode.Disable);
392        renderState = (RenderState) ic.readSavable("renderState", null);
393        usesShaders = ic.readBoolean("usesShaders", false);
394    }
395
396}
397