GLSL support - ShaderObjectsState

Ok, here’s my first commit to jME. After studying jME and some questions to renanse, I’ve made this ShaderObjectsState working. It works very well, but I don’t know if the code is correct for jME (understand that some part of the code could have been made better using existing jME features)… Just tell me what’s wrong and I’ll correct it :slight_smile:



First, here are the modifications to existing code :



Renderer.java


/**
 * <code>createShaderObjectsState</code> retrieves the shader object state object for the
 * proper renderer.
 *
 * @return the <code>ShaderObjectsState</code> object that can make use of the
 *         proper renderer.
 */
public ShaderObjectsState createShaderObjectsState();



LWJGLRenderer.java


/**
 * <code>createShaderObjectsState</code> returns a new LWJGLShaderObjectsState
 * object as a regular ShaderObjectsState.
 *
 * @return an ShaderObjectsState object.
 */
public ShaderObjectsState createShaderObjectsState() {
   return new LWJGLShaderObjectsState();
}



RenderState.java


/** The value returned by getType() for ShaderObjectsState. */
public final static int RS_SHADER_OBJECTS = 14;

/** The total number of diffrent types of RenderState. */
public final static int RS_MAX_STATE = 15;



DisplaySystem.java


/**
 * Called when the display system is created, this function sets the default render states for
 * the renderer.  It should not be called directly by the user.
 * @param r The renderer to get the default states from.
 */
public static void updateStates(Renderer r) {
        // ... //
        Spatial.defaultStateList[RenderState.RS_SHADER_OBJECTS] = r.createShaderObjectsState();
        Spatial.defaultStateList[RenderState.RS_SHADER_OBJECTS].setEnabled(false);
}



DummyDisplaySystem.java


public ShaderObjectsState createShaderObjectsState() {
   return new ShaderObjectsState(){
      public boolean isSupported() {return false;}
                  public void load(URL vert, URL frag) {}
                  public void apply() {}
   };
}
public ShaderObjectsState getShaderObjectsState() {return createShaderObjectsState();}



Matrix3f.java


/**
 * <code>toFloatBuffer</code> returns a FloatBuffer object that contains the
 * matrix data.
 *
 * @return matrix data as a FloatBuffer.
 */
   
public FloatBuffer toFloatBuffer()
{
   FloatBuffer fb = BufferUtils.createFloatBuffer(9);
   fb.put(m00).put(m01).put(m02);
   fb.put(m10).put(m11).put(m12);
   fb.put(m20).put(m21).put(m22);
   fb.rewind();
   return fb;
}



Matrix4f.java


/**
 * <code>toFloatBuffer</code> returns a FloatBuffer object that contains the
 * matrix data.
 *
 * @return matrix data as a FloatBuffer.
 */
   
public FloatBuffer toFloatBuffer()
{
   FloatBuffer fb = BufferUtils.createFloatBuffer(16);
   fb.put(matrix[0]).put(matrix[1]).put(matrix[2]).put(matrix[3]);
   fb.rewind();
   return fb;
}



The state code now :

ShaderObjectsState.java


/*
 * Copyright (c) 2003-2004, jMonkeyEngine - Mojo Monkey Coding
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the Mojo Monkey Coding, jME, jMonkey Engine, nor the
 * names of its contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package com.jme.scene.state;

import java.net.URL;
import java.util.HashMap;

import com.jme.math.Matrix3f;
import com.jme.math.Matrix4f;
import com.jme.util.ShaderUniform;

/**
 * Implementation of the GL_ARB_shader_objects extension.
 * @author Thomas Hourdel
 */
public abstract class ShaderObjectsState extends RenderState {
   
    public HashMap uniforms = new HashMap();
   
    /**
     * <code>isSupported</code> determines if the ARB_shader_objects extension
     * is supported by current graphics configuration.
     * @return if ARB shader objects are supported
     */
    public abstract boolean isSupported();

   /**
    * Set an uniform value for this shader object.
    *
    * @param var uniform variable to change
    * @param value the new value
    */
   
   public void setUniform(String var, int value) {
       ShaderUniform object = new ShaderUniform(var, ShaderUniform.SU_INT);
       object.vint = new int[1];
       object.vint[0] = value;
       uniforms.put(var, object);
   }

   /**
    * Set an uniform value for this shader object.
    *
    * @param var uniform variable to change
    * @param value the new value
    */
   
   public void setUniform(String var, float value) {
       ShaderUniform object = new ShaderUniform(var, ShaderUniform.SU_FLOAT);
       object.vfloat = new float[1];
       object.vfloat[0] = value;
       uniforms.put(var, object);
   }

   /**
    * Set an uniform value for this shader object.
    *
    * @param var uniform variable to change
    * @param value1 the new value
    * @param value2 the new value
    */
   
   public void setUniform(String var, int value1, int value2) {
       ShaderUniform object = new ShaderUniform(var, ShaderUniform.SU_INT2);
       object.vint = new int[2];
       object.vint[0] = value1;
       object.vint[1] = value2;
       uniforms.put(var, object);
   }

   /**
    * Set an uniform value for this shader object.
    *
    * @param var uniform variable to change
    * @param value1 the new value
    * @param value2 the new value
    */
   
   public void setUniform(String var, float value1, float value2) {
       ShaderUniform object = new ShaderUniform(var, ShaderUniform.SU_FLOAT2);
       object.vfloat = new float[2];
       object.vfloat[0] = value1;
       object.vfloat[1] = value2;
       uniforms.put(var, object);
   }

   /**
    * Set an uniform value for this shader object.
    *
    * @param var uniform variable to change
    * @param value1 the new value
    * @param value2 the new value
    * @param value3 the new value
    */
   
   public void setUniform(String var, int value1, int value2, int value3) {
       ShaderUniform object = new ShaderUniform(var, ShaderUniform.SU_INT3);
       object.vint = new int[3];
       object.vint[0] = value1;
       object.vint[1] = value2;
       object.vint[2] = value3;
       uniforms.put(var, object);
   }

   /**
    * Set an uniform value for this shader object.
    *
    * @param var uniform variable to change
    * @param value1 the new value
    * @param value2 the new value
    * @param value3 the new value
    */
   
   public void setUniform(String var, float value1, float value2, float value3) {
       ShaderUniform object = new ShaderUniform(var, ShaderUniform.SU_FLOAT3);
       object.vfloat = new float[3];
       object.vfloat[0] = value1;
       object.vfloat[1] = value2;
       object.vfloat[2] = value3;
       uniforms.put(var, object);
   }

   /**
    * Set an uniform value for this shader object.
    *
    * @param var uniform variable to change
    * @param value1 the new value
    * @param value2 the new value
    * @param value3 the new value
    * @param value4 the new value
    */
   
   public void setUniform(String var, int value1, int value2, int value3, int value4) {
       ShaderUniform object = new ShaderUniform(var, ShaderUniform.SU_INT4);
       object.vint = new int[4];
       object.vint[0] = value1;
       object.vint[1] = value2;
       object.vint[2] = value3;
       object.vint[3] = value4;
       uniforms.put(var, object);
   }

   /**
    * Set an uniform value for this shader object.
    *
    * @param var uniform variable to change
    * @param value1 the new value
    * @param value2 the new value
    * @param value3 the new value
    * @param value4 the new value
    */
   
   public void setUniform(String var, float value1, float value2, float value3, float value4) {
       ShaderUniform object = new ShaderUniform(var, ShaderUniform.SU_FLOAT4);
       object.vfloat = new float[4];
       object.vfloat[0] = value1;
       object.vfloat[1] = value2;
       object.vfloat[2] = value3;
       object.vfloat[3] = value4;
       uniforms.put(var, object);
   }

   /**
    * Set an uniform value for this shader object.
    *
    * @param var uniform variable to change
    * @param value the new value (a float buffer of size 4)
    * @param transpose transpose the matrix ?
    */
   
   public void setUniform(String var, float value[], boolean transpose) {
       if(value.length != 4)
           return;

       ShaderUniform object = new ShaderUniform(var, ShaderUniform.SU_MATRIX2);
       object.matrix2f = new float[4];
       object.matrix2f = value;
       object.transpose = transpose;
       uniforms.put(var, object);
   }

   /**
    * Set an uniform value for this shader object.
    *
    * @param var uniform variable to change
    * @param value the new value
    * @param transpose transpose the matrix ?
    */
   
   public void setUniform(String var, Matrix3f value, boolean transpose) {
       ShaderUniform object = new ShaderUniform(var, ShaderUniform.SU_MATRIX3);
       object.matrix3f = value;
       object.transpose = transpose;
       uniforms.put(var, object);
   }

   /**
    * Set an uniform value for this shader object.
    *
    * @param var uniform variable to change
    * @param value the new value
    * @param transpose transpose the matrix ?
    */
   
   public void setUniform(String var, Matrix4f value, boolean transpose) {
       ShaderUniform object = new ShaderUniform(var, ShaderUniform.SU_MATRIX4);
       object.matrix4f = value;
       object.transpose = transpose;
       uniforms.put(var, object);
   }

   /**
    * Set an uniform value for the shader object.
    *
    * @param var uniform variable to change
    * @param sampler the new value
    */
   
   public void setUniform(String var, com.jme.image.Texture sampler) {
       setUniform(var, sampler.getTextureId());
   }

    /**
     * @return RS_SHADER_OBJECTS
     * @see com.jme.scene.state.RenderState#getType()
     */
    public int getType() {
        return RS_SHADER_OBJECTS;
    }
   
    /**
     * <code>load</code> loads the shader object from the specified file.
     * The program must be in ASCII format. We delegate the loading to each
     * implementation because we do not know in what format the underlying API
     * wants the data.
     * @param vert text file containing the vertex shader object
     * @param frag text file containing the fragment shader object
     */
    public abstract void load(URL vert, URL frag);
}



LWJGLShaderObjectsState.java


/*
 * Copyright (c) 2003-2004, jMonkeyEngine - Mojo Monkey Coding
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the Mojo Monkey Coding, jME, jMonkey Engine, nor the
 * names of its contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package com.jme.scene.state.lwjgl;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.Iterator;
import java.util.logging.Level;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.*;

import com.jme.scene.state.ShaderObjectsState;
import com.jme.util.LoggingSystem;
import com.jme.util.ShaderUniform;

/**
 * Implementation of the GL_ARB_shader_objects extension.
 * @author Thomas Hourdel
 */
public class LWJGLShaderObjectsState extends ShaderObjectsState {
   
    /** OpenGL id for this program. **/
   private int programID = -1;

    /**
     * Determines if the current OpenGL context supports the
     * GL_ARB_shader_objects extension.
     * @see com.jme.scene.state.ShaderObjectsState#isSupported()
     */
    public boolean isSupported() {
        return GLContext.GL_ARB_shader_objects;
    }
   
    /**
    * Get uniform variable location according to his string name.
    *
    * @param name uniform variable name
    */
   private int getUniLoc(String name) {
      ByteBuffer nameBuf = BufferUtils.createByteBuffer(name.getBytes().length);
      nameBuf.clear();
      nameBuf.put(name.getBytes());
      nameBuf.rewind();

      return ARBShaderObjects.glGetUniformLocationARB(programID, nameBuf);
   }
   
    /**
    * Load an URL and grab content into a ByteBuffer.
    *
    * @param url the url to load
    */
   
    private ByteBuffer load(java.net.URL url) {
      try {
         byte shaderCode[] = null;
         ByteBuffer shaderByteBuffer = null;

         BufferedInputStream bufferedInputStream = new BufferedInputStream(url.openStream());
         DataInputStream dataStream = new DataInputStream(bufferedInputStream);
         dataStream.readFully(shaderCode = new byte[bufferedInputStream.available()]);
         bufferedInputStream.close();
         dataStream.close();

         shaderByteBuffer = BufferUtils.createByteBuffer(shaderCode.length);
         shaderByteBuffer.put(shaderCode);
         shaderByteBuffer.rewind();

         return shaderByteBuffer;
      } catch (Exception e) {
         LoggingSystem.getLogger().log(Level.SEVERE,
               "Could not load shader object: " + e);
         LoggingSystem.getLogger().throwing(getClass().getName(),
               "load(URL)", e);
         return null;
      }
   }

    /**
     * Loads the shader object.
     * @see com.jme.scene.state.ShaderObjectsState#load(java.net.URL, java.net.URL)
     */
    public void load(URL vert, URL frag) {
        ByteBuffer vertexByteBuffer = load(vert);
      ByteBuffer fragmentByteBuffer = load(frag);

      if(vertexByteBuffer != null || fragmentByteBuffer != null) {
           // Create the shader objects
         int vertexShaderID = ARBShaderObjects.glCreateShaderObjectARB(ARBVertexShader.GL_VERTEX_SHADER_ARB);
         int fragmentShaderID = ARBShaderObjects.glCreateShaderObjectARB(ARBFragmentShader.GL_FRAGMENT_SHADER_ARB);

           // Create the sources
         ARBShaderObjects.glShaderSourceARB(vertexShaderID, vertexByteBuffer);
         ARBShaderObjects.glShaderSourceARB(fragmentShaderID, fragmentByteBuffer);

           // Compile the vertex shader
         IntBuffer compiled = BufferUtils.createIntBuffer(1);
         ARBShaderObjects.glCompileShaderARB(vertexShaderID);
         ARBShaderObjects.glGetObjectParameterARB(vertexShaderID, ARBShaderObjects.GL_OBJECT_COMPILE_STATUS_ARB, compiled);
         checkProgramError(compiled, vertexShaderID);

           // Compile the fragment shader
         compiled.clear();
         ARBShaderObjects.glCompileShaderARB(fragmentShaderID);
         ARBShaderObjects.glGetObjectParameterARB(fragmentShaderID, ARBShaderObjects.GL_OBJECT_COMPILE_STATUS_ARB, compiled);
         checkProgramError(compiled, fragmentShaderID);

           // Create the program
         programID = ARBShaderObjects.glCreateProgramObjectARB();
         ARBShaderObjects.glAttachObjectARB(programID, vertexShaderID);
         ARBShaderObjects.glAttachObjectARB(programID, fragmentShaderID);
         ARBShaderObjects.glLinkProgramARB(programID);
      }

    }
   
    /**
    * Check for program errors. If an error is detected, program exits.
    *
    * @param compiled the compiler state for a given shader
    * @param id shader's id
    */
   private void checkProgramError(IntBuffer compiled, int id) {

      if(compiled.get(0) == 0) {
         IntBuffer iVal = BufferUtils.createIntBuffer(1);
         ARBShaderObjects.glGetObjectParameterARB(id, ARBShaderObjects.GL_OBJECT_INFO_LOG_LENGTH_ARB, iVal);
         int length = iVal.get();
         String out = null;

         if(length > 0) {
            ByteBuffer infoLog = BufferUtils.createByteBuffer(length);

            iVal.flip();
            ARBShaderObjects.glGetInfoLogARB(id, iVal, infoLog);

            byte[] infoBytes = new byte[length];
            infoLog.get(infoBytes);
            out = new String(infoBytes);
         }

         LoggingSystem.getLogger().log(Level.SEVERE, out);
         System.exit(0);
      }
   }

    /**
     * Applies those shader objects to the current scene. Checks if the
    * GL_ARB_shader_objects extension is supported before attempting to enable
    * those objects.
     * @see com.jme.scene.state.RenderState#apply()
     */
    public void apply() {
        if(isSupported()) {
         if(isEnabled()) {
            if(programID != -1) {
                  // Apply the shader...
               ARBShaderObjects.glUseProgramObjectARB(programID);
               
                  // Assign uniforms...
                if(!uniforms.isEmpty()) {
                    for(Iterator iterator = uniforms.keySet().iterator(); iterator.hasNext();) {
                        ShaderUniform uniformVar = (ShaderUniform)uniforms.get((String)iterator.next());
                        switch(uniformVar.type) {
                            case ShaderUniform.SU_INT:
                                ARBShaderObjects.glUniform1iARB(getUniLoc(uniformVar.name),
                                        uniformVar.vint[0]);
                                break;
                            case ShaderUniform.SU_INT2:
                                ARBShaderObjects.glUniform2iARB(getUniLoc(uniformVar.name),
                                        uniformVar.vint[0],
                                        uniformVar.vint[1]);
                                break;
                            case ShaderUniform.SU_INT3:
                                ARBShaderObjects.glUniform3iARB(getUniLoc(uniformVar.name),
                                        uniformVar.vint[0],
                                        uniformVar.vint[1],
                                        uniformVar.vint[2]);
                                break;
                            case ShaderUniform.SU_INT4:
                                ARBShaderObjects.glUniform4iARB(getUniLoc(uniformVar.name),

                                        uniformVar.vint[0],
                                        uniformVar.vint[1],
                                        uniformVar.vint[2],
                                        uniformVar.vint[3]);
                                break;
                            case ShaderUniform.SU_FLOAT:
                                ARBShaderObjects.glUniform1fARB(getUniLoc(uniformVar.name),
                                        uniformVar.vfloat[0]);
                                break;
                            case ShaderUniform.SU_FLOAT2:
                                ARBShaderObjects.glUniform2fARB(getUniLoc(uniformVar.name),
                                        uniformVar.vfloat[0],
                                        uniformVar.vfloat[1]);
                                break;
                            case ShaderUniform.SU_FLOAT3:
                                ARBShaderObjects.glUniform3fARB(getUniLoc(uniformVar.name),
                                        uniformVar.vfloat[0],
                                        uniformVar.vfloat[1],
                                        uniformVar.vfloat[2]);
                                break;
                            case ShaderUniform.SU_FLOAT4:
                                ARBShaderObjects.glUniform4fARB(getUniLoc(uniformVar.name),
                                        uniformVar.vfloat[0],
                                        uniformVar.vfloat[1],
                                        uniformVar.vfloat[2],
                                        uniformVar.vfloat[3]);
                                break;
                            case ShaderUniform.SU_MATRIX2:
                                final java.nio.FloatBuffer matrix2f = org.lwjgl.BufferUtils.createFloatBuffer(4);
                               matrix2f.clear();
                               matrix2f.put(uniformVar.matrix2f);
                               matrix2f.rewind();
                                ARBShaderObjects.glUniformMatrix2ARB(getUniLoc(uniformVar.name),
                                        uniformVar.transpose,
                                        matrix2f);
                                break;
                            case ShaderUniform.SU_MATRIX3:
                                ARBShaderObjects.glUniformMatrix3ARB(getUniLoc(uniformVar.name),
                                        uniformVar.transpose,
                                        uniformVar.matrix3f.toFloatBuffer());
                                break;
                            case ShaderUniform.SU_MATRIX4:
                                ARBShaderObjects.glUniformMatrix4ARB(getUniLoc(uniformVar.name),
                                        uniformVar.transpose,
                                        uniformVar.matrix4f.toFloatBuffer());
                                break;
                            default: // Sould never happen.
                                break;
                        }
                    }
                }
            }
         } else {
            ARBShaderObjects.glUseProgramObjectARB(0);
         }
      }
    }

}



ShaderUniform.java


/*
 * Copyright (c) 2003-2004, jMonkeyEngine - Mojo Monkey Coding
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the Mojo Monkey Coding, jME, jMonkey Engine, nor the
 * names of its contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package com.jme.util;

import com.jme.math.Matrix3f;
import com.jme.math.Matrix4f;

/**
 * An utily class to store shader's uniform variables content. Used by the <code>ShaderObjectsState</code> class.
 * @author Thomas Hourdel
 * @see com.jme.scene.state.ShaderObjectsState
 */
public class ShaderUniform {

    public final static int SU_INT     = 0;
    public final static int SU_INT2    = 1;
    public final static int SU_INT3    = 2;
    public final static int SU_INT4    = 3;
    public final static int SU_FLOAT   = 4;
    public final static int SU_FLOAT2  = 5;
    public final static int SU_FLOAT3  = 6;
    public final static int SU_FLOAT4  = 7;
    public final static int SU_MATRIX2 = 8;
    public final static int SU_MATRIX3 = 9;
    public final static int SU_MATRIX4 = 10;
   
    /** Name of the uniform variable. **/
    public String name;
   
    /** Type of uniform value. **/
    public int type;
   
    /** For int content. **/
    public int vint[];
   
    /** For float content. **/
    public float vfloat[];
   
    /** Matrix2f storage. **/
    public float matrix2f[];
   
    /** Matrix3f storage. **/
    public Matrix3f matrix3f;
   
    /** Matrix4f storage. **/
    public Matrix4f matrix4f;
   
    /** Used to transpose the matrix if wanted. **/
    public boolean transpose;

    /**
     * Create a new uniform object.
     *
     * @param name uniform's name
     * @param type uniform's value type
     */
    public ShaderUniform(String name, int type) {
        this.name = name;
        this.type = type;
    }

}



TestShaderObjectsState.java


/*
 * Copyright (c) 2003-2004, jMonkeyEngine - Mojo Monkey Coding
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the Mojo Monkey Coding, jME, jMonkey Engine, nor the
 * names of its contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package jmetest.renderer.state;

import com.jme.app.SimpleGame;
import com.jme.input.NodeHandler;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.shape.Quad;
import com.jme.scene.state.ShaderObjectsState;

/**
 * @author Thomas Hourdel
 */
public class TestShaderObjectsState extends SimpleGame {
   
    public static void main(String[] args) {
        TestShaderObjectsState app = new TestShaderObjectsState();
        app.setDialogBehaviour(ALWAYS_SHOW_PROPS_DIALOG);
        app.start();
    }

    protected void simpleInitGame() {
        display.setTitle("GLSL");
        display.getRenderer().setBackgroundColor(new ColorRGBA(0.0f, 0.0f, 0.0f, 0.0f));

        cam.setLocation(new Vector3f(0, 0, 2));
        cam.update();
        input = new NodeHandler(this, rootNode, properties.getRenderer());
        input.setKeySpeed(10);
        input.setMouseSpeed(2);

        Quad brick = createBrickQuad();
        rootNode.attachChild(brick);

        rootNode.updateRenderState();
    }
   
    private Quad createBrickQuad() {
        ShaderObjectsState so = display.getRenderer().createShaderObjectsState();
       
          // Check is GLSL is supported on current hardware.
        if(!so.isSupported())
        {
            display.close();
            System.exit(0);
        }
       
        so.load(TestShaderObjectsState.class.getClassLoader().getResource(
                "jmetest/data/images/shader.vert"),
            TestShaderObjectsState.class.getClassLoader().getResource(
                "jmetest/data/images/shader.frag"));
        so.setUniform("BrickColor", 1.0f, 0.3f, 0.2f);
      so.setUniform("MortarColor", 0.85f, 0.86f, 0.84f);
      so.setUniform("BrickSize", 0.30f, 0.15f);
      so.setUniform("BrickPct", 0.90f, 0.85f);
      so.setUniform("LightPosition", 0.0f, 0.0f, 4.0f);
      so.setEnabled(true);

        //Generate the torus
        Quad box = new Quad("glslQuad", 1f, 1f);
        box.setRenderState(so);

        return box;
    }
}



You can find the shaders for the test here.
Tell me what you think about it, I'll make all the changes needed.

Chman
  1. What would happen if you use this state with the old one at the same time? If you can only use one then they should be the same state.
  2. One thing I liked about the old system was the ability to accesses the light position from the light state in the shadier. Can I do this with this one?
"Badmi" wrote:
1) What would happen if you use this state with the old one at the same time? If you can only use one then they should be the same state.
Good question. I can't answer it because I've nerver used GLSL + ASM shaders at the same time on the same object, but theorically, it couldn't use the two shaders at the same time. It would used the last binded shader, as textures work... I don't think putting all the differents shaders type in a same state would be a good think. GLSL is different from ASM shaders (ARB_vertex_program & ARB_fragment program ones), because GLSL (or Cg or HLSL) defines a shader object which is C-like (no asm). Shader objects are sent to the graphic card and this one compiled it and output an ASM-like code (which is automatically stored in the card, no need to call it directly). Using different states to separate each shader type would make a cleaner code for the user part. Well, that's what I think, but if wanted I could merge all shader states...

2) One thing I liked about the old system was the ability to accesses the light position from the light state in the shadier. Can I do this with this one?

Yes, it's called "uniform fields" in GLSL and Cg. I don't know if you've ever used GLSL, but for example, in the shader code if we have something like that :


uniform vec3 aVector;


You can access it from the state by calling :


shaderObjectsState.setUniform("aVector", 1.0f, 2.0f, 3.0f);


It's the power of shader objects... Damn easy to use compared to ASM code ;) But you're right on one point : I've forgotten to put a getUniform() method... Shouldn't be to hard to implement.

Chman

@Badmi: I don’t think GLSL and the old frag/vert programs are compatible to the point where we could easily combine the two. My two cents anyhow.

They do not have to have the same class only have the same slot in the array. (Same Type number).



Chman: I meant accessing the light from the light state without setting a "uniform fields".

Hi,

thanks for the glsl effort so far.



Basic test seems to work fine, performance = to arb fragments.



Anybody got any ideas on how to set sampler2d and sampler3d via uniforms?

Most of the advanced examples in the 3dlabs demo and shaderdesigner use textures, but I’m a little confused as to the format for initialisation of the sampler2d/3d uniforms for setting texture3d.



eg: Eroded.frag (from 3dlabs glsl demo default folder)

varying float lightIntensity;

varying vec3 Position;



uniform vec3 Offset;

uniform sampler3D sampler3d;



void main (void)

{

vec4 noisevec;

vec3 color;

float intensity;



noisevec = texture3D(sampler3d, 1.2 * (vec3 (0.5) + Position));



intensity = 0.75 * (noisevec.x + noisevec.y + noisevec.z + noisevec.w);



intensity = 1.95 * abs(2.0 * intensity - 1.0);

intensity = clamp(intensity, 0.0, 1.0);



if (intensity < fract(0.5-Offset.x-Offset.y-Offset.z)) discard;



color = mix(vec3 (0.2, 0.1, 0.0), vec3(0.8, 0.8, 0.8), intensity);



color = lightIntensity;

color = clamp(color, 0.0, 1.0);



gl_FragColor = vec4 (color, 1.0);



}





http://nehe.gamedev.net/data/articles/article.asp?article=21

explains a little more (see Using Textures With GLSL at the bottom) but I’m still confused as to the jME method for binding textures to sampler3D and passign via uniform.



thnx,

pc





/
***********************************************************************

  •                                                                   *<br />
    
  •           Copyright (C) 2002-2004  3Dlabs Inc. Ltd.               *<br />
    
  •                                                                   *<br />
    
  •                    All rights reserved.                           *<br />
    
  •                                                                   *<br />
    
  • Redistribution and use in source and binary forms, with or without *
  • modification, are permitted provided that the following conditions *
  • are met: *
  •                                                                   *<br />
    
  • Redistributions of source code must retain the above copyright    *<br />
    
  • notice, this list of conditions and the following disclaimer.     *<br />
    
  •                                                                   *<br />
    
  • Redistributions in binary form must reproduce the above           *<br />
    
  • copyright notice, this list of conditions and the following       *<br />
    
  • disclaimer in the documentation and/or other materials provided   *<br />
    
  • with the distribution.                                            *<br />
    
  •                                                                   *<br />
    
  • Neither the name of 3Dlabs Inc. Ltd. nor the names of its         *<br />
    
  • contributors may be used to endorse or promote products derived   *<br />
    
  • from this software without specific prior written permission.     *<br />
    
  •                                                                   *<br />
    
  • THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
  • "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
  • LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
  • FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
  • COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, *
  • INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, *
  • BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; *
  • LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER *
  • CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT *
  • LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN *
  • ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
  • POSSIBILITY OF SUCH DAMAGE. *
  •                                                                   *<br />
    

************************************************************************/

sorry, but what’s that copyright notice for?

It’s the generic 3dlabs copyright notice for the glsl shader programs included with their glsl demo app, of which the example above (Eroded.frag) is one.

I would say that posting the complete shader code on a forum is a form of redistribution of source code, so I just included the notice as an afterthought. If they are kind enough to provide free examples, the least I can do is respect the terms of their liberal license :stuck_out_tongue:

ta.



Is texture3D (GL_TEXTURE_3D from lwjgl/GL12) implemeted in jME?

I see com.jme.renderer.TextureRenderer has 1D/2D but no 3D?



Excuse the dumb questions, I’m more app side not engine side.

Just wondering if this is why I can’t get this one using sampler3D to work?



thanks,

pc

"sonicviz" wrote:
Just wondering if this is why I can't get this one using sampler3D to work?
Well, as you've said, Jme doesn't seem to support 3D textures yet, that's why you can't use sampler3D...

Chman

I’ve updated the code by adding a new setUniform(String,com.jme.image.Texture) method to make sampler use more user-friendly…



Does someone have comments on this code ? Do you want something more ? Something to change ?

Don’t be afraid of telling me that this code is very bad… :wink:



Chman

I haven’t had time to look too closely at it… I wonder why. :wink: I did, however, take your code and integrate it with a local copy and it ran fine (I saw the quad handle per pixel lighting).



Is it possible for you to take a look at the other shaders (fragment and vertex) states and confirm that they are similar in usage. While I understand why they all can’t be collapsed into one state, I’d at least like to make such that using one is just like using the other. If that is a possibility.

Is it possible for you to take a look at the other shaders (fragment and vertex) states and confirm that they are similar in usage. While I understand why they all can't be collapsed into one state, I'd at least like to make such that using one is just like using the other. If that is a possibility.

Hum... I don't think it'll be possible. Creating the shader object is done the same way you create a vertex_program/fragment_program using the load method, but sending informations to the shader is different. And we won't be able to change that : programs are written in a ASM-like language, so they don't use fields names, just "parameters ID", whereas shader objects handle true fields, as Java do. When accessing an ASM field, you need to specify the field's number. Acessing a shader object's field is done by telling it's name.
Hope I've been clear enough, my english is not so good so sometimes I've some troubles explaining what I want :P

IMO, I think people will stop using ASM language to make shader (it's not very user-friendly compared to a C-like language) and will use CG or GLSL...

So I don't see how I could make the differents shader states works the same way from the user side.... Maybe someone here has a solution to that ?

Chman

Ok, that sounds reasonable. I don’t know nearly enough about shaders to disagree :). At least loading the shader is similar. I can live with the rest. I think I’ll pick up an orange book after Christmas and start playing with GLSL.

Hehe, Orange Book is a GREAT book :wink:



I’m currently trying to run a CG shader file with lwjgl, but it seems to handle it in a strange way : it’s exactly the same as loading a GLSL file, just changing some values… If it works that way I’ll add some code to this ShaderObjectsState so as it would be able to handle GLSL and CG :slight_smile:



Chman

Perhaps we could handle them similarly by using key param names in the vert/frag method that are simply "0", "1" etc. and those are translated back to ints and used to sort and send the associated key-value values to GL. Might be stretching though just to provide a similar name,value interface for the user… so just a thought.

@renanse:

For the user, using a simple String name might be easier, no ? (between I’m not sure I’ve correctly understood your post…)



@mojomonkey:

Cg shaders don’t work this way, and I’ve never seen any source code that use CG with LWJGL. Looking at the doc, it may seem very easy, but the way it told us to do it is broken (org.lwjgl.opengl.EXTCgShader)…



Chman

@chman: yeah, that’s what I’m saying… instead of the vert/frag passing in a list of values only vs. GLSL passing in a list of key/value pairs, we could standardize on key/value pairs for both. The keys behaving as sorting hints for the values in the case of the vert/frag programs.



Maybe I’m off base here? :stuck_out_tongue:

Thinking about this even further, I’m concerned about using the same type slot for both vert/frag and GLSL. for example, you’d only get one default state in that slot, so which would it be? Also, the arguements idea I presented in the sake of unity seems like a good way to throw off high end programmers who already know how to use shaders (those most likely to use it.) Mostly the default state in a single slot scare me. We could get around it with flags and all kinds of code butchering, but I don’t see a reason. Just use two slots and not have GLSL and old vert/frag in use at the same time in the scenegraph.

Here are my thoughts about shaders “unity”… I don’t think I’d be a good think. I’ve already given some reasons in a previous post. I’ll speak on the renanse solution about key/values. It could be an interesting way to code this, but shader programmers are good programmers (you won’t find an opengl beginner that can use ASM shaders), and they know how shaders work. When they’ll use vertex or fragment program, they will use parameters with a param ID, and uniforms with String in GLSL & CG. Changing that for jME would confuse them…

That’s my thought :slight_smile:



Chman