Java tutorial
/** * Copyright (c) 2008-2012 Ardor Labs, Inc. * * This file is part of Ardor3D. * * Ardor3D is free software: you can redistribute it and/or modify it * under the terms of its license which may be found in the accompanying * LICENSE file or at <http://www.ardor3d.com/LICENSE>. */ package com.ardor3d.scene.state.lwjgl; import java.nio.ByteBuffer; import java.nio.IntBuffer; import java.util.logging.Logger; import org.lwjgl.opengl.ARBFragmentShader; import org.lwjgl.opengl.ARBGeometryShader4; import org.lwjgl.opengl.ARBShaderObjects; import org.lwjgl.opengl.ARBTessellationShader; import org.lwjgl.opengl.ARBVertexProgram; import org.lwjgl.opengl.ARBVertexShader; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL20; import com.ardor3d.renderer.ContextCapabilities; import com.ardor3d.renderer.ContextManager; import com.ardor3d.renderer.RenderContext; import com.ardor3d.renderer.lwjgl.LwjglRenderer; import com.ardor3d.renderer.state.GLSLShaderObjectsState; import com.ardor3d.renderer.state.RenderState.StateType; import com.ardor3d.renderer.state.record.ShaderObjectsStateRecord; import com.ardor3d.scene.state.lwjgl.shader.LwjglShaderUtil; import com.ardor3d.util.Ardor3dException; import com.ardor3d.util.geom.BufferUtils; import com.ardor3d.util.shader.ShaderVariable; public abstract class LwjglShaderObjectsStateUtil { private static final Logger logger = Logger.getLogger(LwjglShaderObjectsStateUtil.class.getName()); protected static void sendToGL(final GLSLShaderObjectsState state, final ContextCapabilities caps) { if (state.getVertexShader() == null && state.getFragmentShader() == null) { logger.warning("Could not find shader resources!" + "(both inputbuffers are null)"); state._needSendShader = false; return; } if (state._programID == -1) { state._programID = ARBShaderObjects.glCreateProgramObjectARB(); } if (state.getVertexShader() != null) { if (state._vertexShaderID != -1) { removeVertShader(state); } state._vertexShaderID = ARBShaderObjects.glCreateShaderObjectARB(ARBVertexShader.GL_VERTEX_SHADER_ARB); // Create the sources ARBShaderObjects.glShaderSourceARB(state._vertexShaderID, state.getVertexShader()); // Compile the vertex shader final IntBuffer compiled = BufferUtils.createIntBuffer(1); ARBShaderObjects.glCompileShaderARB(state._vertexShaderID); ARBShaderObjects.glGetObjectParameterARB(state._vertexShaderID, ARBShaderObjects.GL_OBJECT_COMPILE_STATUS_ARB, compiled); checkProgramError(compiled, state._vertexShaderID, state._vertexShaderName); // Attach the program ARBShaderObjects.glAttachObjectARB(state._programID, state._vertexShaderID); } else if (state._vertexShaderID != -1) { removeVertShader(state); state._vertexShaderID = -1; } if (state.getFragmentShader() != null) { if (state._fragmentShaderID != -1) { removeFragShader(state); } state._fragmentShaderID = ARBShaderObjects .glCreateShaderObjectARB(ARBFragmentShader.GL_FRAGMENT_SHADER_ARB); // Create the sources ARBShaderObjects.glShaderSourceARB(state._fragmentShaderID, state.getFragmentShader()); // Compile the fragment shader final IntBuffer compiled = BufferUtils.createIntBuffer(1); ARBShaderObjects.glCompileShaderARB(state._fragmentShaderID); ARBShaderObjects.glGetObjectParameterARB(state._fragmentShaderID, ARBShaderObjects.GL_OBJECT_COMPILE_STATUS_ARB, compiled); checkProgramError(compiled, state._fragmentShaderID, state._fragmentShaderName); // Attach the program ARBShaderObjects.glAttachObjectARB(state._programID, state._fragmentShaderID); } else if (state._fragmentShaderID != -1) { removeFragShader(state); state._fragmentShaderID = -1; } if (caps.isGeometryShader4Supported()) { if (state.getGeometryShader() != null) { if (state._geometryShaderID != -1) { removeGeomShader(state); } state._geometryShaderID = ARBShaderObjects .glCreateShaderObjectARB(ARBGeometryShader4.GL_GEOMETRY_SHADER_ARB); // Create the sources ARBShaderObjects.glShaderSourceARB(state._geometryShaderID, state.getGeometryShader()); // Compile the fragment shader final IntBuffer compiled = BufferUtils.createIntBuffer(1); ARBShaderObjects.glCompileShaderARB(state._geometryShaderID); ARBShaderObjects.glGetObjectParameterARB(state._geometryShaderID, ARBShaderObjects.GL_OBJECT_COMPILE_STATUS_ARB, compiled); checkProgramError(compiled, state._geometryShaderID, state._geometryShaderName); // Attach the program ARBShaderObjects.glAttachObjectARB(state._programID, state._geometryShaderID); } else if (state._geometryShaderID != -1) { removeGeomShader(state); state._geometryShaderID = -1; } } if (caps.isTessellationShadersSupported()) { if (state.getTessellationControlShader() != null) { if (state._tessellationControlShaderID != -1) { removeTessControlShader(state); } state._tessellationControlShaderID = ARBShaderObjects .glCreateShaderObjectARB(ARBTessellationShader.GL_TESS_CONTROL_SHADER); // Create the sources ARBShaderObjects.glShaderSourceARB(state._tessellationControlShaderID, state.getTessellationControlShader()); // Compile the tessellation control shader final IntBuffer compiled = BufferUtils.createIntBuffer(1); ARBShaderObjects.glCompileShaderARB(state._tessellationControlShaderID); ARBShaderObjects.glGetObjectParameterARB(state._tessellationControlShaderID, ARBShaderObjects.GL_OBJECT_COMPILE_STATUS_ARB, compiled); checkProgramError(compiled, state._tessellationControlShaderID, state._tessellationControlShaderName); // Attach the program ARBShaderObjects.glAttachObjectARB(state._programID, state._tessellationControlShaderID); } else if (state._tessellationControlShaderID != -1) { removeTessControlShader(state); state._tessellationControlShaderID = -1; } if (state.getTessellationEvaluationShader() != null) { if (state._tessellationEvaluationShaderID != -1) { removeTessEvalShader(state); } state._tessellationEvaluationShaderID = ARBShaderObjects .glCreateShaderObjectARB(ARBTessellationShader.GL_TESS_CONTROL_SHADER); // Create the sources ARBShaderObjects.glShaderSourceARB(state._tessellationEvaluationShaderID, state.getTessellationEvaluationShader()); // Compile the tessellation control shader final IntBuffer compiled = BufferUtils.createIntBuffer(1); ARBShaderObjects.glCompileShaderARB(state._tessellationEvaluationShaderID); ARBShaderObjects.glGetObjectParameterARB(state._tessellationEvaluationShaderID, ARBShaderObjects.GL_OBJECT_COMPILE_STATUS_ARB, compiled); checkProgramError(compiled, state._tessellationEvaluationShaderID, state._tessellationEvaluationShaderName); // Attach the program ARBShaderObjects.glAttachObjectARB(state._programID, state._tessellationEvaluationShaderID); } else if (state._tessellationEvaluationShaderID != -1) { removeTessEvalShader(state); state._tessellationEvaluationShaderID = -1; } } ARBShaderObjects.glLinkProgramARB(state._programID); checkLinkError(state._programID); state.setNeedsRefresh(true); state._needSendShader = false; } private static void checkLinkError(final int programId) { final IntBuffer compiled = BufferUtils.createIntBuffer(1); ARBShaderObjects.glGetObjectParameterARB(programId, GL20.GL_LINK_STATUS, compiled); if (compiled.get(0) == GL11.GL_FALSE) { ARBShaderObjects.glGetObjectParameterARB(programId, GL20.GL_INFO_LOG_LENGTH, compiled); final int length = compiled.get(0); String out = null; if (length > 0) { final ByteBuffer infoLog = BufferUtils.createByteBuffer(length); ARBShaderObjects.glGetInfoLogARB(programId, compiled, infoLog); final byte[] infoBytes = new byte[length]; infoLog.get(infoBytes); out = new String(infoBytes); } logger.severe(out); throw new Ardor3dException("Error linking GLSL shader: " + out); } } /** Removes the geometry shader */ private static void removeGeomShader(final GLSLShaderObjectsState state) { if (state._geometryShaderID != -1) { ARBShaderObjects.glDetachObjectARB(state._programID, state._geometryShaderID); ARBShaderObjects.glDeleteObjectARB(state._geometryShaderID); } } /** Removes the fragment shader */ private static void removeFragShader(final GLSLShaderObjectsState state) { if (state._fragmentShaderID != -1) { ARBShaderObjects.glDetachObjectARB(state._programID, state._fragmentShaderID); ARBShaderObjects.glDeleteObjectARB(state._fragmentShaderID); } } /** * Removes the vertex shader */ private static void removeVertShader(final GLSLShaderObjectsState state) { if (state._vertexShaderID != -1) { ARBShaderObjects.glDetachObjectARB(state._programID, state._vertexShaderID); ARBShaderObjects.glDeleteObjectARB(state._vertexShaderID); } } /** Removes the tessellation control shader */ private static void removeTessControlShader(final GLSLShaderObjectsState state) { if (state._tessellationControlShaderID != -1) { ARBShaderObjects.glDetachObjectARB(state._programID, state._tessellationControlShaderID); ARBShaderObjects.glDeleteObjectARB(state._tessellationControlShaderID); } } /** Removes the tessellation evaluation shader */ private static void removeTessEvalShader(final GLSLShaderObjectsState state) { if (state._tessellationEvaluationShaderID != -1) { ARBShaderObjects.glDetachObjectARB(state._programID, state._tessellationEvaluationShaderID); ARBShaderObjects.glDeleteObjectARB(state._tessellationEvaluationShaderID); } } /** * 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 static void checkProgramError(final IntBuffer compiled, final int id, final String shaderName) { if (compiled.get(0) == GL11.GL_FALSE) { final IntBuffer iVal = BufferUtils.createIntBuffer(1); ARBShaderObjects.glGetObjectParameterARB(id, ARBShaderObjects.GL_OBJECT_INFO_LOG_LENGTH_ARB, iVal); final int length = iVal.get(0); String out = null; if (length > 0) { final ByteBuffer infoLog = BufferUtils.createByteBuffer(length); ARBShaderObjects.glGetInfoLogARB(id, iVal, infoLog); final byte[] infoBytes = new byte[length]; infoLog.get(infoBytes); out = new String(infoBytes); } logger.severe(out); final String nameString = shaderName.equals("") ? "" : " [ " + shaderName + " ]"; throw new Ardor3dException("Error compiling GLSL shader " + nameString + ": " + out); } } public static void apply(final LwjglRenderer renderer, final GLSLShaderObjectsState state) { final RenderContext context = ContextManager.getCurrentContext(); final ContextCapabilities caps = context.getCapabilities(); if (caps.isGLSLSupported()) { // Ask for the current state record final ShaderObjectsStateRecord record = (ShaderObjectsStateRecord) context .getStateRecord(StateType.GLSLShader); context.setCurrentState(StateType.GLSLShader, state); if (state.isEnabled()) { if (state._needSendShader) { sendToGL(state, caps); } if (state._shaderDataLogic != null) { state._shaderDataLogic.applyData(state, state._mesh, renderer); } } if (!record.isValid() || record.getReference() != state || state.needsRefresh()) { record.setReference(state); if (state.isEnabled() && state._programID != -1) { // clear any previously existing attributes clearEnabledAttributes(record); // set our current shader LwjglShaderUtil.useShaderProgram(state._programID, record); for (int i = state.getShaderAttributes().size(); --i >= 0;) { final ShaderVariable shaderVariable = state.getShaderAttributes().get(i); if (shaderVariable.needsRefresh) { LwjglShaderUtil.updateAttributeLocation(shaderVariable, state._programID); shaderVariable.needsRefresh = false; } LwjglShaderUtil.updateShaderAttribute(renderer, shaderVariable, state.isUseAttributeVBO()); } for (int i = state.getShaderUniforms().size(); --i >= 0;) { final ShaderVariable shaderVariable = state.getShaderUniforms().get(i); if (shaderVariable.needsRefresh) { LwjglShaderUtil.updateUniformLocation(shaderVariable, state._programID); LwjglShaderUtil.updateShaderUniform(shaderVariable); shaderVariable.needsRefresh = false; } } } else { LwjglShaderUtil.useShaderProgram(0, record); clearEnabledAttributes(record); } } if (!record.isValid()) { record.validate(); } } } private static void clearEnabledAttributes(final ShaderObjectsStateRecord record) { // go through and disable any enabled attributes if (!record.enabledAttributes.isEmpty()) { for (int i = 0, maxI = record.enabledAttributes.size(); i < maxI; i++) { final ShaderVariable var = record.enabledAttributes.get(i); if (var.getSize() == 1) { ARBVertexProgram.glDisableVertexAttribArrayARB(var.variableID); } else { for (int j = 0, maxJ = var.getSize(); j < maxJ; j++) { ARBVertexProgram.glDisableVertexAttribArrayARB(var.variableID + j); } } } record.enabledAttributes.clear(); } } }