/**
* Copyright (C) 2013 J.W.Marsden <jmarsden@plural.cc>
*
* 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 cc.plural.ecs.provider;
import cc.plural.ecs.renderer.Shader;
import cc.plural.ecs.renderer.ShaderAttribute;
import cc.plural.ecs.renderer.Uniform;
import cc.plural.math.Matrix4f;
import cc.plural.utils.MatrixPrinter;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL20;
public class StringLWJGLShader extends Shader {
private static final int NO_SHADER = 0;
public boolean inError;
public String vertexCompileError;
public String fragmentCompileError;
public String linkError;
public boolean compiled;
public ShaderAttribute[] attributes;
public Uniform[] uniforms;
public StringLWJGLShader(String vertexShaderSource, String fragmentShaderSource) {
this.vertexShaderSource = vertexShaderSource;
this.fragmentShaderSource = fragmentShaderSource;
this.compiled = false;
this.uniforms = null;
inError = false;
}
public StringLWJGLShader(URL vertexURL, URL fragmentURL) {
StringBuilder vertexSource = new StringBuilder();
StringBuilder fragmentSource = new StringBuilder();
try {
InputStreamReader streamReader = new InputStreamReader(vertexURL.openStream());
BufferedReader reader = new BufferedReader(streamReader);
String line;
while ((line = reader.readLine()) != null) {
vertexSource.append(line).append("\n");
}
reader.close();
streamReader = new InputStreamReader(fragmentURL.openStream());
reader = new BufferedReader(streamReader);
while ((line = reader.readLine()) != null) {
fragmentSource.append(line).append("\n");
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
System.err.println("Could not read file.");
}
this.vertexShaderSource = vertexSource.toString();
this.fragmentShaderSource = fragmentSource.toString();
this.compiled = false;
this.uniforms = null;
inError = false;
}
public boolean inError() {
return inError;
}
public String getVertexCompileError() {
return vertexCompileError;
}
public String getFragmentCompileError() {
return fragmentCompileError;
}
public String getLinkError() {
return linkError;
}
public boolean isInitialized() {
return vertexShaderSource != null && fragmentShaderSource != null;
}
public void init() {
}
public boolean isLoaded() {
return compiled;
}
public void load() {
if (compiled) {
return;
}
vertexShaderHandle = compileShader(GL20.GL_VERTEX_SHADER, vertexShaderSource);
fragmentShaderHandle = compileShader(GL20.GL_FRAGMENT_SHADER, fragmentShaderSource);
programHandle = GL20.glCreateProgram();
if (programHandle == NO_SHADER) {
throw new NullPointerException("Program Create Error?");
}
GL20.glAttachShader(programHandle, vertexShaderHandle);
GL20.glAttachShader(programHandle, fragmentShaderHandle);
GL20.glLinkProgram(programHandle);
ByteBuffer tmp = ByteBuffer.allocateDirect(4);
tmp.order(ByteOrder.nativeOrder());
IntBuffer result = tmp.asIntBuffer();
GL20.glGetProgram(programHandle, GL20.GL_LINK_STATUS, result);
int linked = result.get(NO_SHADER);
if (linked == NO_SHADER) {
linkError = GL20.glGetProgramInfoLog(programHandle, 1000);
System.err.println("Shader Link Error: \"" + linkError + "\"");
inError = true;
} else {
compiled = true;
}
}
public void unLoad() {
if (programHandle != -1) {
GL20.glDeleteProgram(programHandle);
programHandle = -1;
}
if (fragmentShaderHandle != -1) {
GL20.glDeleteShader(fragmentShaderHandle);
fragmentShaderHandle = -1;
}
if (vertexShaderHandle != -1) {
GL20.glDeleteShader(vertexShaderHandle);
vertexShaderHandle = -1;
}
attributes = null;
uniforms = null;
compiled = false;
}
public void release() {
this.vertexShaderSource = null;
this.fragmentShaderSource = null;
}
private int compileShader(int type, String shader) {
IntBuffer result = BufferUtils.createIntBuffer(1);
programHandle = GL20.glCreateShader(type);
GL20.glShaderSource(programHandle, shader);
GL20.glCompileShader(programHandle);
GL20.glGetShader(programHandle, GL20.GL_COMPILE_STATUS, result);
int compiledResult = result.get(NO_SHADER);
if (compiledResult == NO_SHADER) {
if (type == GL20.GL_VERTEX_SHADER) {
vertexCompileError = GL20.glGetShaderInfoLog(programHandle, 1000);
System.err.println("Vertex Shader Compile Error: \"" + vertexCompileError + "\"");
inError = true;
} else if (type == GL20.GL_FRAGMENT_SHADER) {
fragmentCompileError = GL20.glGetShaderInfoLog(programHandle, 1000);
System.err.println("Fragment Shader Compile Error: \"" + fragmentCompileError + "\"");
inError = true;
} else {
System.out.println("Something Wrong Here With Compile:" + shader);
}
}
return programHandle;
}
@Override
public Uniform[] getUniforms() {
if (uniforms != null) {
return uniforms;
}
IntBuffer result = BufferUtils.createIntBuffer(1);
IntBuffer type = BufferUtils.createIntBuffer(2);
GL20.glGetProgram(programHandle, GL20.GL_ACTIVE_UNIFORMS, result);
int numUniforms = result.get(NO_SHADER);
uniforms = new LWJGLUniform[numUniforms];
for (int i = NO_SHADER; i < numUniforms; i++) {
type.clear();
String name = GL20.glGetActiveUniform(programHandle, i, 100, type);
int location = GL20.glGetUniformLocation(programHandle, name);
int typeValue = type.get(NO_SHADER);
Uniform uniform = new LWJGLUniform();
uniform.name = name;
uniform.location = location;
uniform.type = typeValue;
uniforms[i] = uniform;
}
return uniforms;
}
@Override
public ShaderAttribute[] getAttributes() {
if (attributes != null) {
return attributes;
}
IntBuffer result = BufferUtils.createIntBuffer(1);
IntBuffer type = BufferUtils.createIntBuffer(2);
GL20.glGetProgram(programHandle, GL20.GL_ACTIVE_ATTRIBUTES, result);
int numAttributes = result.get(NO_SHADER);
attributes = new ShaderAttribute[numAttributes];
for (int i = NO_SHADER; i < numAttributes; i++) {
type.clear();
String name = GL20.glGetActiveAttrib(programHandle, i, 100, type);
int location = GL20.glGetAttribLocation(programHandle, name);
int typeValue = type.get(NO_SHADER);
ShaderAttribute attribute = new LWJGLShaderAttribute();
attribute.name = name;
attribute.location = location;
attribute.type = typeValue;
attributes[i] = attribute;
}
return attributes;
}
@Override
public boolean hasAttribute(String name) {
if (attributes == null) {
getAttributes();
}
for (ShaderAttribute attribute : attributes) {
if (attribute.name != null && attribute.name.equals(name)) {
return true;
}
}
return false;
}
@Override
public boolean hasAttribute(int location) {
if (attributes == null) {
getAttributes();
}
for (ShaderAttribute attribute : attributes) {
if (attribute.location == location) {
return true;
}
}
return false;
}
@Override
public ShaderAttribute getAttribute(String name) {
if (attributes == null) {
getAttributes();
}
for (ShaderAttribute attribute : attributes) {
if (attribute.name != null && attribute.name.equals(name)) {
return attribute;
}
}
return null;
}
@Override
public int getAttributeLocation(String name) {
if (attributes == null) {
getAttributes();
}
for (ShaderAttribute attribute : attributes) {
if (attribute.name != null && attribute.name.equals(name)) {
return attribute.location;
}
}
return -1;
}
@Override
public ShaderAttribute getAttribute(int location) {
if (attributes == null) {
getAttributes();
}
for (ShaderAttribute attribute : attributes) {
if (attribute.location == location) {
return attribute;
}
}
return null;
}
@Override
public boolean hasUniform(String name) {
for (Uniform uniform : getUniforms()) {
if (uniform.name != null && uniform.name.equals(name)) {
return true;
}
}
return false;
}
@Override
public boolean hasUniform(int location) {
for (Uniform uniform : getUniforms()) {
if (uniform.location == location) {
return true;
}
}
return false;
}
@Override
public Uniform getUniform(String name) {
for (Uniform uniform : getUniforms()) {
if (uniform.name != null && uniform.name.equals(name)) {
return uniform;
}
}
return null;
}
@Override
public Uniform getUniform(int location) {
for (Uniform uniform : getUniforms()) {
if (uniform.location == location) {
return uniform;
}
}
return null;
}
@Override
public int getUniformLocation(String name) {
for (Uniform uniform : getUniforms()) {
if (uniform.name != null && uniform.name.equals(name)) {
return uniform.location;
}
}
return -1;
}
@Override
public void enable() {
if (!isInitialized()) {
init();
}
if (!isLoaded()) {
load();
}
GL20.glUseProgram(programHandle);
}
@Override
public void disable() {
GL20.glUseProgram(NO_SHADER);
}
@Override
public void setUniform(String name, Matrix4f matrix) {
if (!hasUniform(name)) {
return;
}
int location = getUniformLocation(name);
setUniform(location, matrix);
}
@Override
public void setUniform(int location, Matrix4f matrix) {
if (!hasUniform(location)) {
return;
}
FloatBuffer matrix44Buffer = BufferUtils.createFloatBuffer(16);
matrix.loadColumnMajor(matrix44Buffer);
matrix44Buffer.flip();
GL20.glUniformMatrix4(location, false, matrix44Buffer);
}
}