Java tutorial
/* * 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(); } }