org.spout.renderer.lwjgl.gl20.GL20Program.java Source code

Java tutorial

Introduction

Here is the source code for org.spout.renderer.lwjgl.gl20.GL20Program.java

Source

/*
 * This file is part of Caustic LWJGL.
 *
 * Copyright (c) 2013 Spout LLC <http://www.spout.org/>
 * Caustic LWJGL is licensed under the Spout License Version 1.
 *
 * Caustic LWJGL is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * In addition, 180 days after any changes are published, you can use the
 * software, incorporating those changes, under the terms of the MIT license,
 * as described in the Spout License Version 1.
 *
 * Caustic LWJGL 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 Lesser General Public License for
 * more details.
 *
 * You should have received a copy of the GNU Lesser General Public License,
 * the MIT license and the Spout License Version 1 along with this program.
 * If not, see <http://www.gnu.org/licenses/> for the GNU Lesser General Public
 * License and see <http://spout.in/licensev1> for the full license, including
 * the MIT license.
 */
package org.spout.renderer.lwjgl.gl20;

import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.flowpowered.math.matrix.Matrix2f;
import com.flowpowered.math.matrix.Matrix3f;
import com.flowpowered.math.matrix.Matrix4f;
import com.flowpowered.math.vector.Vector2f;
import com.flowpowered.math.vector.Vector3f;
import com.flowpowered.math.vector.Vector4f;

import gnu.trove.impl.Constants;
import gnu.trove.iterator.TObjectIntIterator;
import gnu.trove.map.TIntObjectMap;
import gnu.trove.map.TObjectIntMap;
import gnu.trove.map.hash.TIntObjectHashMap;
import gnu.trove.map.hash.TObjectIntHashMap;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;

import org.spout.renderer.api.gl.Program;
import org.spout.renderer.api.gl.Shader;
import org.spout.renderer.api.util.CausticUtil;
import org.spout.renderer.lwjgl.LWJGLUtil;

/**
 * An OpenGL 2.0 implementation of {@link Program}.
 *
 * @see Program
 */
public class GL20Program extends Program {
    private final Set<Shader> shaders = new HashSet<>();
    // Map of the attribute names to their vao index (optional for GL30 as they can be defined in the shader instead)
    private final TObjectIntMap<String> attributeLayouts = new TObjectIntHashMap<>();
    // Map of the texture units to their names
    private final TIntObjectMap<String> textureLayouts = new TIntObjectHashMap<>();
    // Map of the uniform names to their locations
    private final TObjectIntMap<String> uniforms = new TObjectIntHashMap<>(Constants.DEFAULT_CAPACITY,
            Constants.DEFAULT_LOAD_FACTOR, -1);

    @Override
    public void create() {
        checkNotCreated();
        // Create program
        id = GL20.glCreateProgram();
        // Update the state
        super.create();
    }

    @Override
    public void destroy() {
        checkCreated();
        // Delete the program
        GL20.glDeleteProgram(id);
        // Check for errors
        LWJGLUtil.checkForGLError();
        // Clear the data
        shaders.clear();
        attributeLayouts.clear();
        textureLayouts.clear();
        uniforms.clear();
        // Update the state
        super.destroy();
    }

    @Override
    public void attachShader(Shader shader) {
        checkCreated();
        // Attach the shader
        GL20.glAttachShader(id, shader.getID());
        // Check for errors
        LWJGLUtil.checkForGLError();
        // Add the shader to the set
        shaders.add(shader);
        // Add all attribute and texture layouts
        attributeLayouts.putAll(shader.getAttributeLayouts());
        textureLayouts.putAll(shader.getTextureLayouts());
    }

    @Override
    public void detachShader(Shader shader) {
        checkCreated();
        // Attach the shader
        GL20.glDetachShader(id, shader.getID());
        // Check for errors
        LWJGLUtil.checkForGLError();
        // Remove the shader from the set
        shaders.remove(shader);
        // Remove all attribute and texture layouts
        for (String attribute : shader.getAttributeLayouts().keySet()) {
            attributeLayouts.remove(attribute);
        }
        for (int unit : shader.getTextureLayouts().keys()) {
            textureLayouts.remove(unit);
        }
    }

    @Override
    public void link() {
        checkCreated();
        // Add the attribute layouts to the program state
        final TObjectIntIterator<String> iterator = attributeLayouts.iterator();
        while (iterator.hasNext()) {
            iterator.advance();
            // Bind the index to the name
            GL20.glBindAttribLocation(id, iterator.value(), iterator.key());
        }
        // Link program
        GL20.glLinkProgram(id);
        // Check program link status
        if (GL20.glGetProgrami(id, GL20.GL_LINK_STATUS) == GL11.GL_FALSE) {
            throw new IllegalStateException("Program could not be linked\n" + GL20.glGetProgramInfoLog(id, 1000));
        }
        if (CausticUtil.isDebugEnabled()) {
            // Validate program
            GL20.glValidateProgram(id);
            // Check program validation status
            if (GL20.glGetProgrami(id, GL20.GL_VALIDATE_STATUS) == GL11.GL_FALSE) {
                final Logger logger = CausticUtil.getCausticLogger();
                logger.log(Level.WARNING,
                        "Program validation failed. This doesn''t mean it won''t work, so you maybe able to ignore it\n{0}",
                        GL20.glGetProgramInfoLog(id, 1000));
            }
        }
        // Load uniforms
        uniforms.clear();
        final int uniformCount = GL20.glGetProgrami(id, GL20.GL_ACTIVE_UNIFORMS);
        for (int i = 0; i < uniformCount; i++) {
            final ByteBuffer nameBuffer = CausticUtil.createByteBuffer(256);
            GL20.glGetActiveUniform(id, i, CausticUtil.createIntBuffer(1), CausticUtil.createIntBuffer(1),
                    CausticUtil.createIntBuffer(1), nameBuffer);
            nameBuffer.rewind();
            final byte[] nameBytes = new byte[256];
            nameBuffer.get(nameBytes);
            // Simplify array names
            final String name = new String(nameBytes).trim().replaceFirst("\\[\\d+\\]", "");
            uniforms.put(name, GL20.glGetUniformLocation(id, name));
        }
        // Check for errors
        LWJGLUtil.checkForGLError();
    }

    @Override
    public void use() {
        checkCreated();
        // Bind the program
        GL20.glUseProgram(id);
        // Check for errors
        LWJGLUtil.checkForGLError();
    }

    @Override
    public void bindSampler(int unit) {
        if (!textureLayouts.containsKey(unit)) {
            throw new IllegalArgumentException("No texture layout has been set for the unit: " + unit);
        }
        setUniform(textureLayouts.get(unit), unit);
    }

    // TODO: Support int and boolean vectors
    @Override
    public void setUniform(String name, boolean b) {
        checkCreated();
        if (!uniforms.containsKey(name)) {
            return;
        }
        GL20.glUniform1i(uniforms.get(name), b ? 1 : 0);
        LWJGLUtil.checkForGLError();
    }

    @Override
    public void setUniform(String name, int i) {
        checkCreated();
        if (!uniforms.containsKey(name)) {
            return;
        }
        GL20.glUniform1i(uniforms.get(name), i);
        LWJGLUtil.checkForGLError();
    }

    @Override
    public void setUniform(String name, float f) {
        checkCreated();
        if (!uniforms.containsKey(name)) {
            return;
        }
        GL20.glUniform1f(uniforms.get(name), f);
        LWJGLUtil.checkForGLError();
    }

    @Override
    public void setUniform(String name, float[] fs) {
        checkCreated();
        if (!uniforms.containsKey(name)) {
            return;
        }
        final FloatBuffer floatBuffer = CausticUtil.createFloatBuffer(fs.length);
        for (float f : fs) {
            floatBuffer.put(f);
        }
        floatBuffer.flip();
        GL20.glUniform1(uniforms.get(name), floatBuffer);
        LWJGLUtil.checkForGLError();
    }

    @Override
    public void setUniform(String name, Vector2f v) {
        checkCreated();
        if (!uniforms.containsKey(name)) {
            return;
        }
        GL20.glUniform2f(uniforms.get(name), v.getX(), v.getY());
        LWJGLUtil.checkForGLError();
    }

    @Override
    public void setUniform(String name, Vector2f[] vs) {
        checkCreated();
        if (!uniforms.containsKey(name)) {
            return;
        }
        final FloatBuffer vectorBuffer = CausticUtil.createFloatBuffer(vs.length * 2);
        for (Vector2f v : vs) {
            vectorBuffer.put(v.getX());
            vectorBuffer.put(v.getY());
        }
        vectorBuffer.flip();
        GL20.glUniform2(uniforms.get(name), vectorBuffer);
        LWJGLUtil.checkForGLError();
    }

    @Override
    public void setUniform(String name, Vector3f v) {
        checkCreated();
        if (!uniforms.containsKey(name)) {
            return;
        }
        GL20.glUniform3f(uniforms.get(name), v.getX(), v.getY(), v.getZ());
        LWJGLUtil.checkForGLError();
    }

    @Override
    public void setUniform(String name, Vector3f[] vs) {
        checkCreated();
        if (!uniforms.containsKey(name)) {
            return;
        }
        final FloatBuffer vectorBuffer = CausticUtil.createFloatBuffer(vs.length * 3);
        for (Vector3f v : vs) {
            vectorBuffer.put(v.getX());
            vectorBuffer.put(v.getY());
            vectorBuffer.put(v.getZ());
        }
        vectorBuffer.flip();
        GL20.glUniform3(uniforms.get(name), vectorBuffer);
        LWJGLUtil.checkForGLError();
    }

    @Override
    public void setUniform(String name, Vector4f v) {
        checkCreated();
        if (!uniforms.containsKey(name)) {
            return;
        }
        GL20.glUniform4f(uniforms.get(name), v.getX(), v.getY(), v.getZ(), v.getW());
        LWJGLUtil.checkForGLError();
    }

    @Override
    public void setUniform(String name, Matrix2f m) {
        checkCreated();
        if (!uniforms.containsKey(name)) {
            return;
        }
        final FloatBuffer buffer = CausticUtil.createFloatBuffer(4);
        buffer.put(m.toArray(true));
        buffer.flip();
        GL20.glUniformMatrix2(uniforms.get(name), false, buffer);
        LWJGLUtil.checkForGLError();
    }

    @Override
    public void setUniform(String name, Matrix3f m) {
        checkCreated();
        if (!uniforms.containsKey(name)) {
            return;
        }
        final FloatBuffer buffer = CausticUtil.createFloatBuffer(9);
        buffer.put(m.toArray(true));
        buffer.flip();
        GL20.glUniformMatrix3(uniforms.get(name), false, buffer);
        LWJGLUtil.checkForGLError();
    }

    @Override
    public void setUniform(String name, Matrix4f m) {
        checkCreated();
        if (!uniforms.containsKey(name)) {
            return;
        }
        final FloatBuffer buffer = CausticUtil.createFloatBuffer(16);
        buffer.put(m.toArray(true));
        buffer.flip();
        GL20.glUniformMatrix4(uniforms.get(name), false, buffer);
        LWJGLUtil.checkForGLError();
    }

    @Override
    public Set<Shader> getShaders() {
        return Collections.unmodifiableSet(shaders);
    }

    @Override
    public Set<String> getUniformNames() {
        return Collections.unmodifiableSet(uniforms.keySet());
    }

    @Override
    public GLVersion getGLVersion() {
        return GLVersion.GL20;
    }
}