/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* Copyright 2010 JogAmp Community. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. 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.
*
* THIS SOFTWARE IS PROVIDED BY JogAmp Community ``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 JogAmp Community 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.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of JogAmp Community.
*/
package jogamp.opengl.util.glsl.fixedfunc;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import javax.media.opengl.GL;
import javax.media.opengl.GL2ES1;
import javax.media.opengl.GL2ES2;
import javax.media.opengl.GLArrayData;
import javax.media.opengl.GLException;
import javax.media.opengl.GLUniformData;
import javax.media.opengl.fixedfunc.GLLightingFunc;
import javax.media.opengl.fixedfunc.GLPointerFunc;
import javax.media.opengl.fixedfunc.GLPointerFuncUtil;
import jogamp.opengl.Debug;
import com.jogamp.common.nio.Buffers;
import com.jogamp.common.util.IntIntHashMap;
import com.jogamp.opengl.util.PMVMatrix;
import com.jogamp.opengl.util.glsl.ShaderCode;
import com.jogamp.opengl.util.glsl.ShaderProgram;
import com.jogamp.opengl.util.glsl.ShaderState;
import com.jogamp.opengl.util.glsl.fixedfunc.ShaderSelectionMode;
/**
*
*
* Note: Certain GL FFP state values (e.g.: alphaTestFunc and cullFace)
* are mapped to a lower number range so they can be stored in low precision storage,
* i.e. in a 'lowp int' (GL ES2).
*
*/
public class FixedFuncPipeline {
protected static final boolean DEBUG = Debug.isPropertyDefined("jogl.debug.FixedFuncPipeline", true);
/** The maximum texture units which could be used, depending on {@link ShaderSelectionMode}. */
public static final int MAX_TEXTURE_UNITS = 8;
public static final int MAX_LIGHTS = 8;
public FixedFuncPipeline(GL2ES2 gl, ShaderSelectionMode mode, PMVMatrix pmvMatrix) {
shaderRootClass = FixedFuncPipeline.class;
shaderSrcRoot = shaderSrcRootDef;
shaderBinRoot = shaderBinRootDef;
vertexColorFile = vertexColorFileDef;
vertexColorLightFile = vertexColorLightFileDef;
fragmentColorFile = fragmentColorFileDef;
fragmentColorTextureFile = fragmentColorTextureFileDef;
init(gl, mode, pmvMatrix);
}
public FixedFuncPipeline(GL2ES2 gl, ShaderSelectionMode mode, PMVMatrix pmvMatrix,
Class> shaderRootClass, String shaderSrcRoot,
String shaderBinRoot,
String vertexColorFile, String vertexColorLightFile,
String fragmentColorFile, String fragmentColorTextureFile) {
this.shaderRootClass = shaderRootClass;
this.shaderSrcRoot = shaderSrcRoot;
this.shaderBinRoot = shaderBinRoot;
this.vertexColorFile = vertexColorFile;
this.vertexColorLightFile = vertexColorLightFile;
this.fragmentColorFile = fragmentColorFile;
this.fragmentColorTextureFile = fragmentColorTextureFile;
init(gl, mode, pmvMatrix);
}
public ShaderSelectionMode getShaderSelectionMode() { return shaderSelectionMode; }
public void setShaderSelectionMode(ShaderSelectionMode mode) { shaderSelectionMode=mode; }
public boolean verbose() { return verbose; }
public void setVerbose(boolean v) { verbose = DEBUG || v; }
public boolean isValid() {
return shaderState.linked();
}
public ShaderState getShaderState() {
return shaderState;
}
public int getActiveTextureUnit() {
return activeTextureUnit;
}
public void destroy(GL2ES2 gl) {
if(null != shaderProgramColor) {
shaderProgramColor.release(gl, true);
}
if(null != shaderProgramColorLight) {
shaderProgramColorLight.release(gl, true);
}
if(null != shaderProgramColorTexture2) {
shaderProgramColorTexture2.release(gl, true);
}
if(null != shaderProgramColorTexture4) {
shaderProgramColorTexture4.release(gl, true);
}
if(null != shaderProgramColorTexture4) {
shaderProgramColorTexture4.release(gl, true);
}
if(null != shaderProgramColorTexture8Light) {
shaderProgramColorTexture8Light.release(gl, true);
}
shaderState.destroy(gl);
}
//
// Simple Globals
//
public void glColor4fv(GL2ES2 gl, FloatBuffer data ) {
shaderState.useProgram(gl, true);
GLUniformData ud = shaderState.getUniform(mgl_ColorStatic);
if(null!=ud) {
ud.setData(data);
shaderState.uniform(gl, ud);
}
}
//
// Arrays / States
//
public void glEnableClientState(GL2ES2 gl, int glArrayIndex) {
glToggleClientState(gl, glArrayIndex, true);
}
public void glDisableClientState(GL2ES2 gl, int glArrayIndex) {
glToggleClientState(gl, glArrayIndex, false);
}
private void glToggleClientState(GL2ES2 gl, int glArrayIndex, boolean enable) {
final String arrayName = GLPointerFuncUtil.getPredefinedArrayIndexName(glArrayIndex, clientActiveTextureUnit);
if(null == arrayName) {
throw new GLException("arrayIndex "+toHexString(glArrayIndex)+" unknown");
}
shaderState.useProgram(gl, true);
if(enable) {
shaderState.enableVertexAttribArray(gl, arrayName );
} else {
shaderState.disableVertexAttribArray(gl, arrayName );
}
switch( glArrayIndex ) {
case GLPointerFunc.GL_TEXTURE_COORD_ARRAY:
final int enableV = enable ? 1 : 0;
// enable-bitwise: textureCoordsEnabled |= (1 << clientActiveTextureUnit);
// disable-bitwise: textureCoordsEnabled &= ~(1 << clientActiveTextureUnit);
if ( textureCoordEnabled.get(clientActiveTextureUnit) != enableV) {
textureCoordEnabled.put(clientActiveTextureUnit, enableV);
textureCoordEnabledDirty = true;
}
break;
case GLPointerFunc.GL_COLOR_ARRAY:
colorVAEnabledDirty = true;
break;
}
}
public void glVertexPointer(GL2ES2 gl, GLArrayData data) {
shaderState.useProgram(gl, true);
shaderState.vertexAttribPointer(gl, data);
}
public void glColorPointer(GL2ES2 gl, GLArrayData data) {
shaderState.useProgram(gl, true);
shaderState.vertexAttribPointer(gl, data);
}
public void glNormalPointer(GL2ES2 gl, GLArrayData data) {
shaderState.useProgram(gl, true);
shaderState.vertexAttribPointer(gl, data);
}
//
// MULTI-TEXTURE
//
public void glClientActiveTexture(int textureUnit) {
textureUnit -= GL.GL_TEXTURE0;
if(0 <= textureUnit && textureUnit "+toHexString(ifmt));
}
} else {
System.err.println("FixedFuncPipeline: Unimplemented glTexImage2D: target "+toHexString(target)+", internalformat "+toHexString(internalformat));
}
}
/*
public void glTexImage2D(int target, int level, int internalformat, int width, int height, int border,
int format, int type, long pixels_buffer_offset) {
textureFormat.put(activeTextureUnit, internalformat);
textureFormatDirty = true;
}*/
public void glTexEnvi(int target, int pname, int value) {
if(GL2ES1.GL_TEXTURE_ENV == target && GL2ES1.GL_TEXTURE_ENV_MODE == pname) {
final int mode;
switch( value ) {
case GL2ES1.GL_ADD:
mode = 1;
break;
case GL2ES1.GL_MODULATE:
mode = 2;
break;
case GL2ES1.GL_DECAL:
mode = 3;
break;
case GL2ES1.GL_BLEND:
mode = 4;
break;
case GL2ES1.GL_REPLACE:
mode = 5;
break;
case GL2ES1.GL_COMBINE:
mode = 2; // FIXME
System.err.println("FixedFuncPipeline: glTexEnv GL_TEXTURE_ENV_MODE: unimplemented mode: "+toHexString(value));
break;
default:
throw new GLException("glTexEnv GL_TEXTURE_ENV_MODE: invalid mode: "+toHexString(value));
}
setTextureEnvMode(mode);
} else if(verbose) {
System.err.println("FixedFuncPipeline: Unimplemented TexEnv: target "+toHexString(target)+", pname "+toHexString(pname)+", mode: "+toHexString(value));
}
}
private void setTextureEnvMode(int value) {
if( value != textureEnvMode.get(activeTextureUnit) ) {
textureEnvMode.put(activeTextureUnit, value);
textureEnvModeDirty = true;
}
}
public void glGetTexEnviv(int target, int pname, IntBuffer params) { // FIXME
System.err.println("FixedFuncPipeline: Unimplemented glGetTexEnviv: target "+toHexString(target)+", pname "+toHexString(pname));
}
public void glGetTexEnviv(int target, int pname, int[] params, int params_offset) { // FIXME
System.err.println("FixedFuncPipeline: Unimplemented glGetTexEnviv: target "+toHexString(target)+", pname "+toHexString(pname));
}
//
// Lighting
//
public void glLightfv(GL2ES2 gl, int light, int pname, java.nio.FloatBuffer params) {
shaderState.useProgram(gl, true);
light -=GLLightingFunc.GL_LIGHT0;
if(0 <= light && light < MAX_LIGHTS) {
GLUniformData ud = null;
switch(pname) {
case GLLightingFunc.GL_AMBIENT:
ud = shaderState.getUniform(mgl_LightSource+"["+light+"].ambient");
break;
case GLLightingFunc.GL_DIFFUSE:
ud = shaderState.getUniform(mgl_LightSource+"["+light+"].diffuse");
break;
case GLLightingFunc.GL_SPECULAR:
ud = shaderState.getUniform(mgl_LightSource+"["+light+"].specular");
break;
case GLLightingFunc.GL_POSITION:
ud = shaderState.getUniform(mgl_LightSource+"["+light+"].position");
break;
case GLLightingFunc.GL_SPOT_DIRECTION:
ud = shaderState.getUniform(mgl_LightSource+"["+light+"].spotDirection");
break;
case GLLightingFunc.GL_SPOT_EXPONENT:
ud = shaderState.getUniform(mgl_LightSource+"["+light+"].spotExponent");
break;
case GLLightingFunc.GL_SPOT_CUTOFF:
ud = shaderState.getUniform(mgl_LightSource+"["+light+"].spotCutoff");
break;
case GLLightingFunc.GL_CONSTANT_ATTENUATION:
ud = shaderState.getUniform(mgl_LightSource+"["+light+"].constantAttenuation");
break;
case GLLightingFunc.GL_LINEAR_ATTENUATION:
ud = shaderState.getUniform(mgl_LightSource+"["+light+"].linearAttenuation");
break;
case GLLightingFunc.GL_QUADRATIC_ATTENUATION:
ud = shaderState.getUniform(mgl_LightSource+"["+light+"].quadraticAttenuation");
break;
default:
throw new GLException("glLightfv invalid pname: "+toHexString(pname));
}
if(null!=ud) {
ud.setData(params);
shaderState.uniform(gl, ud);
}
} else {
throw new GLException("glLightfv light not within [0.."+MAX_LIGHTS+"]: "+light);
}
}
public void glMaterialfv(GL2ES2 gl, int face, int pname, java.nio.FloatBuffer params) {
shaderState.useProgram(gl, true);
switch (face) {
case GL.GL_FRONT:
case GL.GL_FRONT_AND_BACK:
break;
case GL.GL_BACK:
System.err.println("FixedFuncPipeline: Unimplemented glMaterialfv GL_BACK face");
return;
default:
}
GLUniformData ud = null;
switch(pname) {
case GLLightingFunc.GL_AMBIENT:
ud = shaderState.getUniform(mgl_FrontMaterial+".ambient");
break;
case GLLightingFunc.GL_AMBIENT_AND_DIFFUSE:
{
ud = shaderState.getUniform(mgl_FrontMaterial+".ambient");
if(null!=ud) {
ud.setData(params);
shaderState.uniform(gl, ud);
}
}
// fall through intended ..
case GLLightingFunc.GL_DIFFUSE:
ud = shaderState.getUniform(mgl_FrontMaterial+".diffuse");
break;
case GLLightingFunc.GL_SPECULAR:
ud = shaderState.getUniform(mgl_FrontMaterial+".specular");
break;
case GLLightingFunc.GL_EMISSION:
ud = shaderState.getUniform(mgl_FrontMaterial+".emission");
break;
case GLLightingFunc.GL_SHININESS:
ud = shaderState.getUniform(mgl_FrontMaterial+".shininess");
break;
default:
throw new GLException("glMaterialfv invalid pname: "+toHexString(pname));
}
if(null!=ud) {
ud.setData(params);
shaderState.uniform(gl, ud);
} else if(verbose) {
}
}
//
// Misc States
//
public void glShadeModel(GL2ES2 gl, int mode) {
shaderState.useProgram(gl, true);
GLUniformData ud = shaderState.getUniform(mgl_ShadeModel);
if(null!=ud) {
ud.setData(mode);
shaderState.uniform(gl, ud);
}
}
public void glCullFace(int faceName) {
int _cullFace;
switch(faceName) {
case GL.GL_FRONT:
_cullFace = 1;
break;
case GL.GL_BACK:
_cullFace = 2;
break;
case GL.GL_FRONT_AND_BACK:
_cullFace = 3;
break;
default:
throw new GLException("glCullFace invalid faceName: "+toHexString(faceName));
}
if(0 < _cullFace) {
if(0>cullFace) {
_cullFace *= -1;
}
if(cullFace != _cullFace) {
cullFace = _cullFace;
cullFaceDirty=true;
}
}
}
public void glAlphaFunc(int func, float ref) {
int _func;
switch(func) {
case GL.GL_NEVER:
_func = 1;
break;
case GL.GL_LESS:
_func = 2;
break;
case GL.GL_EQUAL:
_func = 3;
break;
case GL.GL_LEQUAL:
_func = 4;
break;
case GL.GL_GREATER:
_func = 5;
break;
case GL.GL_NOTEQUAL:
_func = 6;
break;
case GL.GL_GEQUAL:
_func = 7;
break;
case GL.GL_ALWAYS:
_func = 8;
break;
default:
throw new GLException("glAlphaFunc invalid func: "+toHexString(func));
}
if(0 < _func) {
if(0>alphaTestFunc) {
_func *= -1;
}
if( alphaTestFunc != _func || alphaTestRef != ref ) {
alphaTestFunc = _func;
alphaTestRef = ref;
alphaTestDirty=true;
}
}
}
/**
* @return false if digested in regard to GL2ES2 spec,
* eg this call must not be passed to an underlying ES2 implementation.
* true if this call shall be passed to an underlying GL2ES2/ES2 implementation as well.
*/
public boolean glEnable(int cap, boolean enable) {
switch(cap) {
case GL.GL_BLEND:
case GL.GL_DEPTH_TEST:
case GL.GL_DITHER:
case GL.GL_POLYGON_OFFSET_FILL:
case GL.GL_SAMPLE_ALPHA_TO_COVERAGE:
case GL.GL_SAMPLE_COVERAGE:
case GL.GL_SCISSOR_TEST:
case GL.GL_STENCIL_TEST:
return true;
case GL.GL_CULL_FACE:
final int _cullFace;
if(0>cullFace && enable || 0alphaTestFunc && enable || 0
private static final String mgl_TexCoordEnabled = "mgl_TexCoordEnabled"; // int mgl_TexCoordEnabled[MAX_TEXTURE_UNITS];
private static final String mgl_TexEnvMode = "mgl_TexEnvMode"; // int mgl_TexEnvMode[MAX_TEXTURE_UNITS];
private static final String mgl_TexFormat = "mgl_TexFormat"; // int mgl_TexFormat[MAX_TEXTURE_UNITS];
// private static final FloatBuffer zero4f = Buffers.newDirectFloatBuffer(new float[] { 0.0f, 0.0f, 0.0f, 0.0f });
private static final FloatBuffer neut4f = Buffers.newDirectFloatBuffer(new float[] { 0.0f, 0.0f, 0.0f, 1.0f });
private static final FloatBuffer one4f = Buffers.newDirectFloatBuffer(new float[] { 1.0f, 1.0f, 1.0f, 1.0f });
public static final FloatBuffer defAmbient = neut4f;
public static final FloatBuffer defDiffuseN = neut4f;
public static final FloatBuffer defSpecularN = neut4f;
public static final FloatBuffer defPosition = Buffers.newDirectFloatBuffer(new float[] { 0f, 0f, 1f, 0f });
public static final FloatBuffer defSpotDir = Buffers.newDirectFloatBuffer(new float[] { 0f, 0f, -1f });
public static final float defSpotExponent = 0f;
public static final float defSpotCutoff = 180f;
public static final float defConstantAtten = 1f;
public static final float defLinearAtten = 0f;
public static final float defQuadraticAtten = 0f;
public static final FloatBuffer defLightModelAmbient = Buffers.newDirectFloatBuffer(new float[] { 0.2f, 0.2f, 0.2f, 1.0f });
public static final FloatBuffer defMatAmbient = Buffers.newDirectFloatBuffer(new float[] { 0.2f, 0.2f, 0.2f, 1.0f });
public static final FloatBuffer defMatDiffuse = Buffers.newDirectFloatBuffer(new float[] { 0.8f, 0.8f, 0.8f, 1.0f });
public static final FloatBuffer defMatSpecular = neut4f;
public static final FloatBuffer defMatEmission = neut4f;
public static final float defMatShininess = 0f;
private static final String vertexColorFileDef = "FixedFuncColor";
private static final String vertexColorLightFileDef = "FixedFuncColorLight";
private static final String fragmentColorFileDef = "FixedFuncColor";
private static final String fragmentColorTextureFileDef = "FixedFuncColorTexture";
private static final String shaderSrcRootDef = "shaders" ;
private static final String shaderBinRootDef = "shaders/bin" ;
private final Class> shaderRootClass;
private final String shaderSrcRoot;
private final String shaderBinRoot;
private final String vertexColorFile;
private final String vertexColorLightFile;
private final String fragmentColorFile;
private final String fragmentColorTextureFile;
}