
com.flowpowered.caustic.lwjgl.gl20.GL20Program Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of caustic-lwjgl Show documentation
Show all versions of caustic-lwjgl Show documentation
LWJGL implementation of the Caustic rendering library API.
The newest version!
/*
* This file is part of Caustic LWJGL, licensed under the MIT License (MIT).
*
* Copyright (c) 2013 Flow Powered
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.flowpowered.caustic.lwjgl.gl20;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
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 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 com.flowpowered.caustic.api.gl.Program;
import com.flowpowered.caustic.api.gl.Shader;
import com.flowpowered.caustic.api.util.CausticUtil;
import com.flowpowered.caustic.lwjgl.LWJGLUtil;
/**
* An OpenGL 2.0 implementation of {@link Program}.
*
* @see Program
*/
public class GL20Program extends Program {
// Represents an unset value for a uniform
private static final Object UNSET = new Object() {
@Override
public String toString() {
return "UNSET";
}
};
// Set of all shaders in this program
private final Set 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 attributeLayouts = new TObjectIntHashMap<>();
// Map of the texture units to their names
private final TIntObjectMap textureLayouts = new TIntObjectHashMap<>();
// Map of the uniform names to their locations
private final TObjectIntMap uniforms = new TObjectIntHashMap<>(Constants.DEFAULT_CAPACITY, Constants.DEFAULT_LOAD_FACTOR, -1);
// Map of the uniform names to their values
private final Map uniformValues = new HashMap<>();
@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();
uniformValues.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 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);
final int maxLength = GL20.glGetProgrami(id, GL20.GL_ACTIVE_UNIFORM_MAX_LENGTH);
final IntBuffer lengthBuffer = CausticUtil.createIntBuffer(1);
final IntBuffer ignored1 = CausticUtil.createIntBuffer(1);
final IntBuffer ignored2 = CausticUtil.createIntBuffer(1);
final ByteBuffer nameBuffer = CausticUtil.createByteBuffer(maxLength);
final byte[] nameBytes = new byte[maxLength];
for (int i = 0; i < uniformCount; i++) {
lengthBuffer.clear();
ignored1.clear();
ignored2.clear();
nameBuffer.clear();
GL20.glGetActiveUniform(id, i, lengthBuffer, ignored1, ignored2, nameBuffer);
final int length = lengthBuffer.get();
nameBuffer.get(nameBytes, 0, length);
// Simplify array names
final String name = new String(nameBytes, 0, length).replaceFirst("\\[\\d+\\]", "");
uniforms.put(name, GL20.glGetUniformLocation(id, name));
uniformValues.put(name, UNSET);
}
// 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 (!isDirty(name, b)) {
return;
}
GL20.glUniform1i(uniforms.get(name), b ? 1 : 0);
uniformValues.put(name, b);
LWJGLUtil.checkForGLError();
}
@Override
public void setUniform(String name, int i) {
checkCreated();
if (!isDirty(name, i)) {
return;
}
GL20.glUniform1i(uniforms.get(name), i);
uniformValues.put(name, i);
LWJGLUtil.checkForGLError();
}
@Override
public void setUniform(String name, float f) {
checkCreated();
if (!isDirty(name, f)) {
return;
}
GL20.glUniform1f(uniforms.get(name), f);
uniformValues.put(name, f);
LWJGLUtil.checkForGLError();
}
@Override
public void setUniform(String name, float[] fs) {
checkCreated();
if (!isDirty(name, fs)) {
return;
}
final FloatBuffer floatBuffer = CausticUtil.createFloatBuffer(fs.length);
for (float f : fs) {
floatBuffer.put(f);
}
floatBuffer.flip();
GL20.glUniform1(uniforms.get(name), floatBuffer);
uniformValues.put(name, fs);
LWJGLUtil.checkForGLError();
}
@Override
public void setUniform(String name, Vector2f v) {
checkCreated();
if (!isDirty(name, v)) {
return;
}
GL20.glUniform2f(uniforms.get(name), v.getX(), v.getY());
uniformValues.put(name, v);
LWJGLUtil.checkForGLError();
}
@Override
public void setUniform(String name, Vector2f[] vs) {
checkCreated();
if (!isDirty(name, vs)) {
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);
uniformValues.put(name, vs);
LWJGLUtil.checkForGLError();
}
@Override
public void setUniform(String name, Vector3f v) {
checkCreated();
if (!isDirty(name, v)) {
return;
}
GL20.glUniform3f(uniforms.get(name), v.getX(), v.getY(), v.getZ());
uniformValues.put(name, v);
LWJGLUtil.checkForGLError();
}
@Override
public void setUniform(String name, Vector3f[] vs) {
checkCreated();
if (!isDirty(name, vs)) {
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);
uniformValues.put(name, vs);
LWJGLUtil.checkForGLError();
}
@Override
public void setUniform(String name, Vector4f v) {
checkCreated();
if (!isDirty(name, v)) {
return;
}
GL20.glUniform4f(uniforms.get(name), v.getX(), v.getY(), v.getZ(), v.getW());
uniformValues.put(name, v);
LWJGLUtil.checkForGLError();
}
@Override
public void setUniform(String name, Matrix2f m) {
checkCreated();
if (!isDirty(name, m)) {
return;
}
final FloatBuffer buffer = CausticUtil.createFloatBuffer(4);
buffer.put(m.toArray(true));
buffer.flip();
GL20.glUniformMatrix2(uniforms.get(name), false, buffer);
uniformValues.put(name, m);
LWJGLUtil.checkForGLError();
}
@Override
public void setUniform(String name, Matrix3f m) {
checkCreated();
if (!isDirty(name, m)) {
return;
}
final FloatBuffer buffer = CausticUtil.createFloatBuffer(9);
buffer.put(m.toArray(true));
buffer.flip();
GL20.glUniformMatrix3(uniforms.get(name), false, buffer);
uniformValues.put(name, m);
LWJGLUtil.checkForGLError();
}
@Override
public void setUniform(String name, Matrix4f m) {
checkCreated();
if (!isDirty(name, m)) {
return;
}
final FloatBuffer buffer = CausticUtil.createFloatBuffer(16);
buffer.put(m.toArray(true));
buffer.flip();
GL20.glUniformMatrix4(uniforms.get(name), false, buffer);
uniformValues.put(name, m);
LWJGLUtil.checkForGLError();
}
private boolean isDirty(String name, Object newValue) {
final Object oldValue = uniformValues.get(name);
return oldValue != null && (oldValue == UNSET || !oldValue.equals(newValue));
}
@Override
public Set getShaders() {
return Collections.unmodifiableSet(shaders);
}
@Override
public Set getUniformNames() {
return Collections.unmodifiableSet(uniforms.keySet());
}
@Override
public GLVersion getGLVersion() {
return GLVersion.GL20;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy