/** * Copyright 2012 Jason Sorensen (sorensenj@smert.net) * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. */ package net.smert.frameworkgl.opengl.shader; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import net.smert.frameworkgl.Files; import net.smert.frameworkgl.Fw; import net.smert.frameworkgl.opengl.GL; import net.smert.frameworkgl.opengl.Shader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * @author Jason Sorensen <sorensenj@smert.net> */ public class ShaderBuilder { private final static Logger log = LoggerFactory.getLogger(ShaderBuilder.class); private final List<Integer> shaderIDs; private final Map<Integer, List<ShaderNameAndSource>> shaderTypeToShaderNameAndSources; private Shader shader; public ShaderBuilder() { shaderIDs = new ArrayList<>(); shaderTypeToShaderNameAndSources = new HashMap<>(); shader = null; } private int compileShader(String shaderName, String shaderSource, int shaderType) { int shaderID = GL.shaderHelper.createShader(shaderType); log.debug("Created a new shader with ID: {}", shaderID); if (shaderID == 0) { throw new RuntimeException("Unable to create shader: Name: " + shaderName + " Type: " + shaderType); } GL.shaderHelper.setSource(shaderID, shaderSource); GL.shaderHelper.compile(shaderID); if (!GL.shaderHelper.getCompileStatus(shaderID)) { log.error("Shader souce code error: Name: {} Info Log:\n{}", shaderName, GL.shaderHelper.getInfoLog(shaderID)); throw new RuntimeException("Shader \"" + shaderName + "\" had compile errors"); } return shaderID; } private String read(String filename) throws IOException { Files.FileAsset fileAsset = Fw.files.getShader(filename); StringBuilder shaderSource = new StringBuilder(8192); try (InputStream is = fileAsset.openStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader reader = new BufferedReader(isr)) { String line; while ((line = reader.readLine()) != null) { shaderSource.append(line).append("\n"); } } return shaderSource.toString(); } public ShaderBuilder buildProgram(String programName) { return buildProgram(programName, GL.defaultAttribLocations.getAttribLocations()); } public ShaderBuilder buildProgram(String programName, Map<Integer, String> attribLocations) { shader = GL.glFactory.createShader(); shader.create(); int programID = shader.getProgramID(); if (programID == 0) { throw new RuntimeException("Unable to create program: Name: " + programName); } // Attach all shaders for (int shaderID : shaderIDs) { GL.shaderHelper.attach(programID, shaderID); } // Bind all attribute locations if (attribLocations != null) { Iterator<Map.Entry<Integer, String>> iterator = attribLocations.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<Integer, String> entry = iterator.next(); GL.shaderHelper.bindAttribLocation(programID, entry.getKey(), entry.getValue()); } } // Link shaders GL.shaderHelper.link(programID); if (!GL.shaderHelper.getLinkStatus(programID)) { log.error("Program linking errors: Name: {} Info Log:\n{}", programName, GL.shaderHelper.getInfoLog(programID)); throw new RuntimeException("Program \"" + programName + "\" had linking errors"); } // Validate program if (Fw.config.isValidateShaders()) { GL.shaderHelper.validate(programID); if (!GL.shaderHelper.getValidateStatus(programID)) { log.error("Program validate errors: Name: {} Info Log:\n{}", programName, GL.shaderHelper.getInfoLog(programID)); throw new RuntimeException("Program \"" + programName + "\" had validate errors"); } } log.info("Loaded shader program: {}", programName); // Delete attached shaders for (int shaderID : shaderIDs) { GL.shaderHelper.detach(programID, shaderID); GL.shaderHelper.deleteShader(shaderID); } return this; } public ShaderBuilder compileShaders() { Iterator<Map.Entry<Integer, List<ShaderNameAndSource>>> iterator = shaderTypeToShaderNameAndSources.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<Integer, List<ShaderNameAndSource>> entry = iterator.next(); int shaderType = entry.getKey(); List<ShaderNameAndSource> shaderNamesAndSources = entry.getValue(); for (ShaderNameAndSource shaderNameAndSource : shaderNamesAndSources) { String shaderName = shaderNameAndSource.getName(); String shaderSource = shaderNameAndSource.getSource(); int shaderID = compileShader(shaderName, shaderSource, shaderType); shaderIDs.add(shaderID); } } return this; } public Shader createShader(boolean reset) { Shader temp = shader; if (reset) { reset(); } return temp; } public ShaderBuilder load(String shaderName, int shaderType) throws IOException { String shaderSource = read(shaderName); List<ShaderNameAndSource> shaderNamesAndSources = shaderTypeToShaderNameAndSources.get(shaderType); if (shaderNamesAndSources == null) { shaderNamesAndSources = new ArrayList<>(); shaderTypeToShaderNameAndSources.put(shaderType, shaderNamesAndSources); } shaderNamesAndSources.add(new ShaderNameAndSource(shaderName, shaderSource)); return this; } public final void reset() { shaderIDs.clear(); shaderTypeToShaderNameAndSources.clear(); shader = null; } private static class ShaderNameAndSource { private final String name; private final String source; public ShaderNameAndSource(String name, String source) { this.name = name; this.source = source; } public String getName() { return name; } public String getSource() { return source; } } }