com.jogamp.graph.curve.opengl.Renderer Maven / Gradle / Ivy
/**
* 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 com.jogamp.graph.curve.opengl;
import java.nio.FloatBuffer;
import javax.media.opengl.GL2ES2;
import javax.media.opengl.GLException;
import javax.media.opengl.fixedfunc.GLMatrixFunc;
import com.jogamp.opengl.util.glsl.ShaderCode;
import com.jogamp.opengl.util.glsl.ShaderState;
import com.jogamp.opengl.util.PMVMatrix;
import com.jogamp.graph.curve.Region;
public abstract class Renderer {
protected static final boolean DEBUG = Region.DEBUG;
protected static final boolean DEBUG_INSTANCE = Region.DEBUG_INSTANCE;
public static boolean isWeightValid(float v) {
return 0.0f <= v && v <= 1.9f ;
}
protected final int renderModes;
protected int vp_width;
protected int vp_height;
protected boolean initialized;
protected final RenderState rs;
private boolean vboSupported = false;
public final boolean isInitialized() { return initialized; }
public final int getWidth() { return vp_width; }
public final int getHeight() { return vp_height; }
public float getWeight() { return rs.getWeight().floatValue(); }
public float getAlpha() { return rs.getAlpha().floatValue(); }
public final PMVMatrix getMatrix() { return rs.pmvMatrix(); }
/**
* Implementation shall load, compile and link the shader program and leave it active.
* @param gl referencing the current GLContext to which the ShaderState is bound to
* @return
*/
protected abstract boolean initShaderProgram(GL2ES2 gl);
protected abstract void destroyImpl(GL2ES2 gl);
/**
* @param rs the used {@link RenderState}
* @param renderModes bit-field of modes
*/
protected Renderer(RenderState rs, int renderModes) {
this.rs = rs;
this.renderModes = renderModes;
}
public final int getRenderModes() {
return renderModes;
}
public boolean usesVariableCurveWeight() { return Region.isNonUniformWeight(renderModes); }
/**
* @return true if Region's renderModes contains all bits as this Renderer's renderModes
* except {@link Region#VARIABLE_CURVE_WEIGHT_BIT}, otherwise false.
*/
public final boolean areRenderModesCompatible(Region region) {
final int cleanRenderModes = getRenderModes() & ( Region.VARIABLE_CURVE_WEIGHT_BIT );
return cleanRenderModes == ( region.getRenderModes() & cleanRenderModes );
}
public final boolean isVBOSupported() { return vboSupported; }
/**
* Initialize shader and bindings for GPU based rendering bound to the given GL object's GLContext
* if not initialized yet.
* Leaves the renderer enabled, ie ShaderState.
* Shall be called by a {@code draw()} method, e.g. {@link RegionRenderer#draw(GL2ES2, Region, float[], int)}
*
* @param gl referencing the current GLContext to which the ShaderState is bound to
* @throws GLException if initialization failed
*/
public final void init(GL2ES2 gl) throws GLException {
if(initialized){
return;
}
vboSupported = gl.isFunctionAvailable("glGenBuffers") &&
gl.isFunctionAvailable("glBindBuffer") &&
gl.isFunctionAvailable("glBufferData") &&
gl.isFunctionAvailable("glDrawElements") &&
gl.isFunctionAvailable("glVertexAttribPointer") &&
gl.isFunctionAvailable("glDeleteBuffers");
if(DEBUG) {
System.err.println("TextRendererImpl01: VBO Supported = " + isVBOSupported());
}
if(!vboSupported){
throw new GLException("VBO not supported");
}
rs.attachTo(gl);
gl.glEnable(GL2ES2.GL_BLEND);
gl.glBlendFunc(GL2ES2.GL_SRC_ALPHA, GL2ES2.GL_ONE_MINUS_SRC_ALPHA); // FIXME: alpha blending stage ?
initialized = initShaderProgram(gl);
if(!initialized) {
throw new GLException("Shader initialization failed");
}
if(!rs.getShaderState().uniform(gl, rs.getPMVMatrix())) {
throw new GLException("Error setting PMVMatrix in shader: "+rs.getShaderState());
}
if( Region.isNonUniformWeight( getRenderModes() ) ) {
if(!rs.getShaderState().uniform(gl, rs.getWeight())) {
throw new GLException("Error setting weight in shader: "+rs.getShaderState());
}
}
if(!rs.getShaderState().uniform(gl, rs.getAlpha())) {
throw new GLException("Error setting global alpha in shader: "+rs.getShaderState());
}
if(!rs.getShaderState().uniform(gl, rs.getColorStatic())) {
throw new GLException("Error setting global color in shader: "+rs.getShaderState());
}
}
public final void flushCache(GL2ES2 gl) {
// FIXME: REMOVE !
}
public void destroy(GL2ES2 gl) {
if(!initialized){
if(DEBUG_INSTANCE) {
System.err.println("TextRenderer: Not initialized!");
}
return;
}
rs.getShaderState().useProgram(gl, false);
destroyImpl(gl);
rs.destroy(gl);
initialized = false;
}
public final RenderState getRenderState() { return rs; }
public final ShaderState getShaderState() { return rs.getShaderState(); }
public final void enable(GL2ES2 gl, boolean enable) {
rs.getShaderState().useProgram(gl, enable);
}
public void setWeight(GL2ES2 gl, float v) {
if( !isWeightValid(v) ) {
throw new IllegalArgumentException("Weight out of range");
}
rs.getWeight().setData(v);
if(null != gl && rs.getShaderState().inUse() && Region.isNonUniformWeight( getRenderModes() ) ) {
rs.getShaderState().uniform(gl, rs.getWeight());
}
}
public void setAlpha(GL2ES2 gl, float alpha_t) {
rs.getAlpha().setData(alpha_t);
if(null != gl && rs.getShaderState().inUse()) {
rs.getShaderState().uniform(gl, rs.getAlpha());
}
}
public void getColorStatic(GL2ES2 gl, float[] rgb) {
FloatBuffer fb = (FloatBuffer) rs.getColorStatic().getBuffer();
rgb[0] = fb.get(0);
rgb[1] = fb.get(1);
rgb[2] = fb.get(2);
}
public void setColorStatic(GL2ES2 gl, float r, float g, float b){
FloatBuffer fb = (FloatBuffer) rs.getColorStatic().getBuffer();
fb.put(0, r);
fb.put(1, g);
fb.put(2, b);
if(null != gl && rs.getShaderState().inUse()) {
rs.getShaderState().uniform(gl, rs.getColorStatic());
}
}
public void rotate(GL2ES2 gl, float angle, float x, float y, float z) {
rs.pmvMatrix().glRotatef(angle, x, y, z);
updateMatrix(gl);
}
public void translate(GL2ES2 gl, float x, float y, float z) {
rs.pmvMatrix().glTranslatef(x, y, z);
updateMatrix(gl);
}
public void scale(GL2ES2 gl, float x, float y, float z) {
rs.pmvMatrix().glScalef(x, y, z);
updateMatrix(gl);
}
public void resetModelview(GL2ES2 gl) {
rs.pmvMatrix().glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
rs.pmvMatrix().glLoadIdentity();
updateMatrix(gl);
}
public void updateMatrix(GL2ES2 gl) {
if(initialized && null != gl && rs.getShaderState().inUse()) {
rs.getShaderState().uniform(gl, rs.getPMVMatrix());
}
}
public boolean reshapePerspective(GL2ES2 gl, float angle, int width, int height, float near, float far) {
this.vp_width = width;
this.vp_height = height;
final float ratio = (float)width/(float)height;
final PMVMatrix p = rs.pmvMatrix();
p.glMatrixMode(GLMatrixFunc.GL_PROJECTION);
p.glLoadIdentity();
p.gluPerspective(angle, ratio, near, far);
updateMatrix(gl);
return true;
}
public boolean reshapeOrtho(GL2ES2 gl, int width, int height, float near, float far) {
this.vp_width = width;
this.vp_height = height;
final PMVMatrix p = rs.pmvMatrix();
p.glMatrixMode(GLMatrixFunc.GL_PROJECTION);
p.glLoadIdentity();
p.glOrthof(0, width, 0, height, near, far);
updateMatrix(gl);
return true;
}
protected String getVertexShaderName() {
return "curverenderer" + getImplVersion();
}
protected String getFragmentShaderName() {
final String version = getImplVersion();
final String pass = Region.isVBAA(renderModes) ? "-2pass" : "-1pass" ;
final String weight = Region.isNonUniformWeight(renderModes) ? "-weight" : "" ;
return "curverenderer" + version + pass + weight;
}
// FIXME: Really required to have sampler2D def. precision ? If not, we can drop getFragmentShaderPrecision(..) and use default ShaderCode ..
public static final String es2_precision_fp = "\nprecision mediump float;\nprecision mediump int;\nprecision mediump sampler2D;\n";
protected String getFragmentShaderPrecision(GL2ES2 gl) {
if( gl.isGLES2() ) {
return es2_precision_fp;
}
if( ShaderCode.requiresGL3DefaultPrecision(gl) ) {
return ShaderCode.gl3_default_precision_fp;
}
return null;
}
protected String getImplVersion() {
return "01";
}
}