/*
* This file is part of the Haven & Hearth game client.
* Copyright (C) 2009 Fredrik Tolf <fredrik@dolda2000.com>, and
* Björn Johannessen <johannessen.bjorn@gmail.com>
*
* Redistribution and/or modification of this file is subject to the
* terms of the GNU Lesser General Public License, version 3, as
* published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
*
* Other parts of this source tree adhere to other copying
* rights. Please see the file `COPYING' in the root directory of the
* source tree for details.
*
* A copy the GNU Lesser General Public License is distributed along
* with the source tree of which this file is a part in the file
* `doc/LPGL-3'. If it is missing for any reason, please see the Free
* Software Foundation's website at <http://www.fsf.org/>, or write
* to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA
*/
package haven;
import java.util.*;
import javax.media.opengl.*;
import haven.GLShader.VertexShader;
import haven.GLShader.FragmentShader;
public class GLProgram implements java.io.Serializable {
public final Collection<GLShader> shaders;
private transient ProgOb glp;
public GLProgram(Collection<GLShader> shaders) {
this.shaders = new ArrayList<GLShader>(shaders);
}
/* Meaningful function is meaningful. :-P */
private static Collection<GLShader> collapse(GLShader[][] shaders) {
Collection<GLShader> sc = new ArrayList<GLShader>();
for(int i = 0; i < shaders.length; i++) {
if(shaders[i] == null)
continue;
for(int o = 0; o < shaders[i].length; o++)
sc.add(shaders[i][o]);
}
return(sc);
}
public GLProgram(GLShader[][] shaders) {
this(collapse(shaders));
makemains(this.shaders);
}
private static void makemains(Collection<GLShader> shaders) {
List<VertexShader> vs = new ArrayList<VertexShader>();
List<FragmentShader> fs = new ArrayList<FragmentShader>();
for(GLShader s : shaders) {
if(s instanceof VertexShader)
vs.add((VertexShader)s);
else if(s instanceof FragmentShader)
fs.add((FragmentShader)s);
}
shaders.add(VertexShader.makemain(vs));
shaders.add(FragmentShader.makemain(fs));
}
public static class ProgOb extends GLObject {
public final int id;
public ProgOb(GL2 gl) {
super(gl);
id = gl.glCreateProgramObjectARB();
}
public void delete() {
gl.glDeleteObjectARB(id);
}
public void link(GLProgram prog) {
for(GLShader sh : prog.shaders)
gl.glAttachShader(id, sh.glid(gl));
gl.glLinkProgram(id);
int[] buf = {0};
gl.glGetObjectParameterivARB(id, GL2.GL_OBJECT_LINK_STATUS_ARB, buf, 0);
if(buf[0] != 1) {
String info = null;
gl.glGetObjectParameterivARB(id, GL2.GL_OBJECT_INFO_LOG_LENGTH_ARB, buf, 0);
if(buf[0] > 0) {
byte[] logbuf = new byte[buf[0]];
gl.glGetInfoLogARB(id, logbuf.length, buf, 0, logbuf, 0);
/* The "platform's default charset" is probably a reasonable choice. */
info = new String(logbuf, 0, buf[0]);
}
throw(new ProgramException("Failed to link GL program", prog, info));
}
}
public int uniform(String name) {
int r = gl.glGetUniformLocationARB(id, name);
if(r < 0)
throw(new RuntimeException("Unknown uniform name: " + name));
return(r);
}
public int attrib(String name) {
int r = gl.glGetAttribLocation(id, name);
if(r < 0)
throw(new RuntimeException("Unknown uniform name: " + name));
return(r);
}
}
public static class ProgramException extends RuntimeException {
public final GLProgram program;
public final String info;
public ProgramException(String msg, GLProgram program, String info) {
super(msg);
this.program = program;
this.info = info;
}
public String toString() {
if(info == null)
return(super.toString());
else
return(super.toString() + "\nLog:\n" + info);
}
}
public void apply(GOut g) {
synchronized(this) {
if((glp != null) && (glp.gl != g.gl))
dispose();
if(glp == null) {
glp = new ProgOb(g.gl);
glp.link(this);
}
g.gl.glUseProgramObjectARB(glp.id);
}
}
public void dispose() {
synchronized(this) {
if(glp != null) {
ProgOb cur = glp;
glp = null;
cur.dispose();
}
umap.clear();
amap.clear();
}
}
private final Map<String, Integer> umap = new IdentityHashMap<String, Integer>();
public int uniform(String name) {
Integer r = umap.get(name);
if(r == null)
umap.put(name, r = new Integer(glp.uniform(name)));
return(r.intValue());
}
private final Map<String, Integer> amap = new IdentityHashMap<String, Integer>();
public int attrib(String name) {
Integer r = amap.get(name);
if(r == null)
amap.put(name, r = new Integer(glp.attrib(name)));
return(r.intValue());
}
}