/* * This file is part of Goko. * * Goko is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Goko is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Goko. If not, see <http://www.gnu.org/licenses/>. */ package org.goko.tools.viewer.jogl.shaders; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.util.HashMap; import java.util.Map; import javax.media.opengl.GL; import javax.media.opengl.GL3; import javax.media.opengl.fixedfunc.GLMatrixFunc; import javax.media.opengl.glu.GLU; import javax.vecmath.Color4f; import javax.vecmath.Point3f; import org.apache.commons.collections.MapUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.goko.core.log.GkLog; import org.goko.tools.viewer.jogl.utils.light.Light; import com.jogamp.opengl.util.PMVMatrix; public class ShaderLoader { private static final GkLog LOG = GkLog.getLogger(ShaderLoader.class); private static ShaderLoader instance; private Map<EnumGokoShaderProgram, Integer> mapShaderByType; private ShaderLoader(){ this.mapShaderByType = new HashMap<EnumGokoShaderProgram, Integer>(); } public static ShaderLoader getInstance(){ if(instance == null){ instance = new ShaderLoader(); } return instance; } public static int loadShader(GL3 gl, EnumGokoShaderProgram enumGokoShaderProgram){ return getInstance().loadShaderIntern(gl, enumGokoShaderProgram); } private int loadShaderIntern(GL3 gl, EnumGokoShaderProgram enumGokoShaderProgram){ int shaderProgram = 0; if(mapShaderByType.containsKey(enumGokoShaderProgram)){ shaderProgram = mapShaderByType.get(enumGokoShaderProgram); }else{ shaderProgram = loadShader(gl, getClass().getResourceAsStream(enumGokoShaderProgram.getVertexShaderPath()), getClass().getResourceAsStream(enumGokoShaderProgram.getFragmentShaderPath())); mapShaderByType.put(enumGokoShaderProgram, shaderProgram); } return shaderProgram; } /** * Update the projection matrix in every loaded shader * @param gl gl context * @param matrix the camera matrix */ public void updateProjectionMatrix(GL3 gl, PMVMatrix matrix){ matrix.glMatrixMode(GLMatrixFunc.GL_PROJECTION); if(MapUtils.isNotEmpty(mapShaderByType)){ for (EnumGokoShaderProgram enumShaderProgram : mapShaderByType.keySet()) { Integer shaderProgram = mapShaderByType.get(enumShaderProgram); gl.glUseProgram(shaderProgram); int shaderProjectionMatrixId = gl.glGetUniformLocation(shaderProgram, "projectionMatrix"); if(shaderProjectionMatrixId >= 0){ gl.glUniformMatrix4fv(shaderProjectionMatrixId, 1, false, matrix.glGetPMatrixf()); } } } gl.glUseProgram(0); } public void updateLightData(GL3 gl, Light light0, Light light1){ if(MapUtils.isNotEmpty(mapShaderByType)){ for (EnumGokoShaderProgram enumShaderProgram : mapShaderByType.keySet()) { Integer shaderProgram = mapShaderByType.get(enumShaderProgram); gl.glUseProgram(shaderProgram); // int hack = gl.glGetUniformLocation(shaderProgram, "material.ambient"); // if(hack >= 0){ // int ambient = gl.glGetUniformLocation(shaderProgram, "material.ambient"); // gl.glUniform3fv(ambient, 1, new float[]{0.2f,0.4f,0.2f},0); // int diffuse = gl.glGetUniformLocation(shaderProgram, "material.diffuse"); // gl.glUniform3fv(diffuse, 1, new float[]{0.0f,1f,0},0); // int specular = gl.glGetUniformLocation(shaderProgram, "material.specular"); // gl.glUniform3fv(specular, 1, new float[]{0.0f,0,0.5f},0); // } // First light if(light0 != null){ int light0ShaderId = gl.glGetUniformLocation(shaderProgram, "iLight0Position"); int diffuse0ShaderId = gl.glGetUniformLocation(shaderProgram, "iLight0Diffuse"); int ambientShaderId = gl.glGetUniformLocation(shaderProgram, "iLight0Ambient"); if(light0ShaderId >= 0){ Point3f position = light0.getPosition(); gl.glUniform4fv(light0ShaderId, 1, new float[]{position.x,position.y,position.z,1},0); } if(diffuse0ShaderId >= 0){ Color4f diffuse = light0.getDiffuse(); gl.glUniform4fv(diffuse0ShaderId, 1, new float[]{diffuse.x,diffuse.y,diffuse.z,diffuse.w},0); } if(ambientShaderId >= 0){ Color4f ambient = light0.getAmbient(); gl.glUniform4fv(ambientShaderId, 1, new float[]{ambient.x,ambient.y,ambient.z,ambient.w},0); } } // Second light if(light1 != null){ int light1ShaderId = gl.glGetUniformLocation(shaderProgram, "iLight1Position"); // Renvoi -1 si le champs n'est pas utilis� dans le shader int diffuse1ShaderId = gl.glGetUniformLocation(shaderProgram, "iLight1Diffuse"); if(light1ShaderId >= 0){ Point3f p = light1.getPosition(); gl.glUniform4fv(light1ShaderId, 1, new float[]{p.x,p.y,p.z,1},0); } if(diffuse1ShaderId >= 0){ Color4f c = light1.getDiffuse(); gl.glUniform4fv(diffuse1ShaderId, 1, new float[]{c.x,c.y,c.z,c.w},0); } } } } } // a faire : // - tester la possibilit� de changer les features sans les installer/desinstaller protected int loadShader(GL3 gl, InputStream vertexShaderInputStream, InputStream fragmentShaderInputStream){ int vertexShader = gl.glCreateShader(GL3.GL_VERTEX_SHADER); int fragmentShader = gl.glCreateShader(GL3.GL_FRAGMENT_SHADER); String vertexShaderSource = getStringFromInputStream(vertexShaderInputStream); gl.glShaderSource(vertexShader, 1, new String[]{vertexShaderSource}, (int[])null, 0); gl.glCompileShader(vertexShader); String fragmentShaderSource = getStringFromInputStream(fragmentShaderInputStream); gl.glShaderSource(fragmentShader, 1, new String[]{fragmentShaderSource}, (int[])null, 0); gl.glCompileShader(fragmentShader); int shaderProgram = gl.glCreateProgram(); gl.glAttachShader(shaderProgram, vertexShader); gl.glAttachShader(shaderProgram, fragmentShader); gl.glLinkProgram(shaderProgram); checkGlError("Link :", gl); checkLinkProgramm(gl, shaderProgram); checkCompiling(gl, vertexShader); checkCompiling(gl, fragmentShader); gl.glValidateProgram(shaderProgram); checkValidateProgramm(gl, shaderProgram); checkGlError("Validate :", gl); return shaderProgram; } private static void checkGlError(String str, GL3 gl){ int errorCheckValue = gl.glGetError(); if (errorCheckValue != GL.GL_NO_ERROR) { GLU glu = new GLU(); LOG.error(str+glu.gluErrorString(errorCheckValue)); } } private static void checkLinkProgramm(GL3 gl, int program){ IntBuffer b = IntBuffer.allocate(10); gl.glGetProgramiv(program, GL3.GL_LINK_STATUS, b); ByteBuffer logBuffer = ByteBuffer.allocate(10000); gl.glGetProgramInfoLog(program, 10000, null, logBuffer); try { String logBufferStr = new String(logBuffer.array(), "UTF-8"); if(StringUtils.isNotBlank(StringUtils.defaultString(logBufferStr).trim())){ LOG.error("CheckProgramm :"+logBufferStr); } } catch (UnsupportedEncodingException e) { LOG.error(e); } } private static void checkValidateProgramm(GL3 gl, int program){ IntBuffer b = IntBuffer.allocate(10); gl.glGetProgramiv(program, GL3.GL_VALIDATE_STATUS, b); ByteBuffer logBuffer = ByteBuffer.allocate(10000); gl.glGetProgramInfoLog(program, 10000, null, logBuffer); try { String logBufferStr = new String(logBuffer.array(), "UTF-8"); if(StringUtils.isNotBlank(StringUtils.defaultString(logBufferStr).trim())){ LOG.error("Check validate programm :"+logBufferStr); } } catch (UnsupportedEncodingException e) { LOG.error(e); } } private static void checkCompiling(GL3 gl, int shader){ IntBuffer b = IntBuffer.allocate(10); gl.glGetShaderiv(shader, GL3.GL_COMPILE_STATUS, b); ByteBuffer logBuffer = ByteBuffer.allocate(10000); gl.glGetShaderInfoLog(shader, 10000, null, logBuffer); try { String logBufferStr = new String(logBuffer.array(), "UTF-8"); if(StringUtils.isNotBlank(StringUtils.defaultString(logBufferStr).trim())){ LOG.error("CheckCompiling :"+logBufferStr); } } catch (UnsupportedEncodingException e) { LOG.error(e); } } private static String getStringFromInputStream(InputStream is) { StringWriter writer = new StringWriter(); try { IOUtils.copy(is, writer); } catch (IOException e1) { e1.printStackTrace(); } return writer.toString(); } }