/*
* 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.nio.*;
import java.util.*;
import javax.media.opengl.*;
public class VertexBuf {
public static final GLState.Slot<Binding> bound = new GLState.Slot<Binding>(GLState.Slot.Type.GEOM, Binding.class);
public final AttribArray[] bufs;
public final int num;
public VertexBuf(AttribArray... bufs) {
AttribArray[] na = new AttribArray[bufs.length];
na[0] = bufs[0];
int num = na[0].size();
for(int i = 1; i < bufs.length; i++) {
na[i] = bufs[i];
if(na[i].size() != num)
throw(new RuntimeException("Buffer sizes do not match"));
}
this.bufs = na;
this.num = num;
}
public <T extends AttribArray> T buf(Class<T> type) {
for(AttribArray a : bufs) {
if(type.isInstance(a))
return(type.cast(a));
}
return(null);
}
public abstract class Binding extends GLState {
public void prep(GLState.Buffer buf) {
buf.put(bound, this);
}
}
public class MemBinding extends Binding {
public void apply(GOut g) {
for(int i = 0; i < bufs.length; i++) {
if(bufs[i] instanceof GLArray)
((GLArray)bufs[i]).bind(g, false);
}
}
public void unapply(GOut g) {
for(int i = 0; i < bufs.length; i++) {
if(bufs[i] instanceof GLArray)
((GLArray)bufs[i]).unbind(g);
}
}
}
public abstract static class AttribArray {
public final int n;
public AttribArray(int n) {
this.n = n;
}
public abstract Buffer data();
public abstract Buffer direct();
public abstract int elsize();
public int size() {
Buffer b = data();
b.rewind();
return(b.capacity() / this.n);
}
/* XXX: It would be terribly nice if GLArray could be a
* multiply inhereted class and these could be put in it
* instead; but alas, this is Java. QQ */
private GLBuffer bufobj;
private int bufmode = GL.GL_STATIC_DRAW;
private boolean update = false;
public void bindvbo(GOut g) {
GL2 gl = g.gl;
synchronized(this) {
if((bufobj != null) && (bufobj.gl != gl))
dispose();
if(bufobj == null) {
bufobj = new GLBuffer(gl);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, bufobj.id);
Buffer data = data();
data.rewind();
gl.glBufferData(GL.GL_ARRAY_BUFFER, data.remaining() * elsize(), data, bufmode);
GOut.checkerr(gl);
update = false;
} else if(update) {
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, bufobj.id);
Buffer data = data();
data.rewind();
gl.glBufferData(GL.GL_ARRAY_BUFFER, data.remaining() * elsize(), data, bufmode);
update = false;
} else {
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, bufobj.id);
}
}
}
public void vbomode(int mode) {
bufmode = mode;
dispose();
}
public void dispose() {
synchronized(this) {
if(bufobj != null) {
bufobj.dispose();
bufobj = null;
}
}
}
public void update() {
update = true;
}
}
public static interface GLArray {
public void bind(GOut g, boolean asvbo);
public void unbind(GOut g);
public Object progid(GOut g);
}
public abstract static class FloatArray extends AttribArray {
public FloatBuffer data;
public FloatArray(int n, FloatBuffer data) {
super(n);
data.rewind();
if(data.capacity() % n != 0)
throw(new RuntimeException(String.format("float-array length %d does not match element count %d", data.capacity(), n)));
this.data = data;
}
public FloatBuffer data() {return(data);}
public FloatBuffer direct() {
if(!data.isDirect())
data = Utils.bufcp(data);
return(data);
}
public int elsize() {return(4);}
}
public abstract static class IntArray extends AttribArray {
public IntBuffer data;
public IntArray(int n, IntBuffer data) {
super(n);
data.rewind();
if(data.capacity() % n != 0)
throw(new RuntimeException(String.format("int-array length %d does not match element count %d", data.capacity(), n)));
this.data = data;
}
public IntBuffer data() {return(data);}
public IntBuffer direct() {
if(!data.isDirect())
data = Utils.bufcp(data);
return(data);
}
public int elsize() {return(4);}
}
public static class VertexArray extends FloatArray implements GLArray {
public VertexArray(FloatBuffer data) {
super(3, data);
}
public VertexArray dup() {return(new VertexArray(Utils.bufcp(data)));}
public void bind(GOut g, boolean asvbo) {
GL2 gl = g.gl;
if(asvbo) {
bindvbo(g);
gl.glVertexPointer(3, GL.GL_FLOAT, 0, 0);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
} else {
data.rewind();
gl.glVertexPointer(3, GL.GL_FLOAT, 0, direct());
}
gl.glEnableClientState(GL2.GL_VERTEX_ARRAY);
}
public void unbind(GOut g) {
g.gl.glDisableClientState(GL2.GL_VERTEX_ARRAY);
}
public Object progid(GOut g) {return(null);}
/* XXX: For compatibility only. Remove whenever possible. */
public void bind(GOut g) {bind(g, false);}
}
public static class NormalArray extends FloatArray implements GLArray {
public NormalArray(FloatBuffer data) {
super(3, data);
}
public NormalArray dup() {return(new NormalArray(Utils.bufcp(data)));}
public void bind(GOut g, boolean asvbo) {
GL2 gl = g.gl;
if(asvbo) {
bindvbo(g);
gl.glNormalPointer(GL.GL_FLOAT, 0, 0);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
} else {
data.rewind();
gl.glNormalPointer(GL.GL_FLOAT, 0, direct());
}
gl.glEnableClientState(GL2.GL_NORMAL_ARRAY);
}
public void unbind(GOut g) {
g.gl.glDisableClientState(GL2.GL_NORMAL_ARRAY);
}
public Object progid(GOut g) {return(null);}
/* XXX: For compatibility only. Remove whenever possible. */
public void bind(GOut g) {bind(g, false);}
}
public static class ColorArray extends FloatArray implements GLArray {
public ColorArray(FloatBuffer data) {
super(4, data);
}
public ColorArray dup() {return(new ColorArray(Utils.bufcp(data)));}
public void bind(GOut g, boolean asvbo) {
GL2 gl = g.gl;
if(asvbo) {
bindvbo(g);
gl.glColorPointer(4, GL.GL_FLOAT, 0, 0);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
} else {
data.rewind();
gl.glColorPointer(4, GL.GL_FLOAT, 0, direct());
}
gl.glEnableClientState(GL2.GL_COLOR_ARRAY);
}
public void unbind(GOut g) {
g.gl.glDisableClientState(GL2.GL_COLOR_ARRAY);
}
public Object progid(GOut g) {return(null);}
}
public static class TexelArray extends FloatArray implements GLArray {
public TexelArray(FloatBuffer data) {
super(2, data);
}
public TexelArray dup() {return(new TexelArray(Utils.bufcp(data)));}
public void bind(GOut g, boolean asvbo) {
GL2 gl = g.gl;
if(asvbo) {
bindvbo(g);
gl.glTexCoordPointer(2, GL.GL_FLOAT, 0, 0);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
} else {
data.rewind();
gl.glTexCoordPointer(2, GL.GL_FLOAT, 0, direct());
}
gl.glEnableClientState(GL2.GL_TEXTURE_COORD_ARRAY);
}
public void unbind(GOut g) {
g.gl.glDisableClientState(GL2.GL_TEXTURE_COORD_ARRAY);
}
public Object progid(GOut g) {return(null);}
}
public static class NamedFloatArray extends FloatArray implements GLArray {
public final haven.glsl.Attribute attr;
private int bound = -1;
public NamedFloatArray(int n, FloatBuffer data, haven.glsl.Attribute attr) {
super(n, data);
this.attr = attr;
}
public void bind(GOut g, boolean asvbo) {
if(g.st.prog != null) {
if((bound = g.st.prog.cattrib(attr)) != -1) {
GL2 gl = g.gl;
if(asvbo) {
bindvbo(g);
gl.glVertexAttribPointer(bound, n, GL2.GL_FLOAT, false, 0, 0);
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);
} else {
data.rewind();
gl.glVertexAttribPointer(bound, n, GL2.GL_FLOAT, false, 0, direct());
}
gl.glEnableVertexAttribArray(bound);
}
}
}
public void unbind(GOut g) {
if(bound != -1) {
g.gl.glDisableVertexAttribArray(bound);
bound = -1;
}
}
public Object progid(GOut g) {
if(g.st.prog == null)
return(null);
return(Integer.valueOf(g.st.prog.cattrib(attr)));
}
}
public static class Vec1Array extends NamedFloatArray implements GLArray {
public Vec1Array(FloatBuffer data, haven.glsl.Attribute attr) {
super(1, data, attr);
}
}
public static class Vec2Array extends NamedFloatArray implements GLArray {
public Vec2Array(FloatBuffer data, haven.glsl.Attribute attr) {
super(2, data, attr);
}
}
public static class Vec3Array extends NamedFloatArray implements GLArray {
public Vec3Array(FloatBuffer data, haven.glsl.Attribute attr) {
super(3, data, attr);
}
}
public static class Vec4Array extends NamedFloatArray implements GLArray {
public Vec4Array(FloatBuffer data, haven.glsl.Attribute attr) {
super(4, data, attr);
}
}
public void dispose() {
for(AttribArray buf : bufs)
buf.dispose();
}
@Resource.LayerName("vbuf")
public static class VertexRes extends Resource.Layer {
public transient final VertexBuf b;
public VertexRes(Resource res, byte[] buf) {
res.super();
ArrayList<AttribArray> bufs = new ArrayList<AttribArray>();
int fl = Utils.ub(buf[0]);
int num = Utils.uint16d(buf, 1);
int off = 3;
while(off < buf.length) {
int id = Utils.ub(buf[off++]);
if(id == 0) {
FloatBuffer data = Utils.wfbuf(num * 3);
for(int i = 0; i < num * 3; i++)
data.put((float)Utils.floatd(buf, off + (i * 5)));
off += num * 5 * 3;
bufs.add(new VertexArray(data));
} else if(id == 1) {
FloatBuffer data = Utils.wfbuf(num * 3);
for(int i = 0; i < num * 3; i++)
data.put((float)Utils.floatd(buf, off + (i * 5)));
off += num * 5 * 3;
bufs.add(new NormalArray(data));
} else if(id == 2) {
FloatBuffer data = Utils.wfbuf(num * 2);
for(int i = 0; i < num * 2; i++)
data.put((float)Utils.floatd(buf, off + (i * 5)));
off += num * 5 * 2;
bufs.add(new TexelArray(data));
} else if(id == 3) {
int mba = Utils.ub(buf[off++]);
IntBuffer ba = Utils.wibuf(num * mba);
for(int i = 0; i < num * mba; i++)
ba.put(-1);
ba.rewind();
FloatBuffer bw = Utils.wfbuf(num * mba);
int[] na = new int[num];
List<String> bones = new ArrayList<String>();
while(true) {
int[] ob = {off};
String bone = Utils.strd(buf, ob);
off = ob[0];
if(bone.length() == 0)
break;
int bidx = bones.size();
bones.add(bone);
while(true) {
int run = Utils.uint16d(buf, off); off += 2;
int st = Utils.uint16d(buf, off); off += 2;
if(run == 0)
break;
for(int i = 0; i < run; i++) {
float w = (float)Utils.floatd(buf, off);
off += 5;
int v = i + st;
int cna = na[v]++;
if(cna >= mba)
continue;
bw.put(v * mba + cna, w);
ba.put(v * mba + cna, bidx);
}
}
}
normweights(bw, ba, mba);
bufs.add(new PoseMorph.BoneArray(mba, ba, bones.toArray(new String[0])));
bufs.add(new PoseMorph.WeightArray(mba, bw));
}
}
this.b = new VertexBuf(bufs.toArray(new AttribArray[0]));
}
private static void normweights(FloatBuffer bw, IntBuffer ba, int mba) {
int i = 0;
while(i < bw.capacity()) {
float tw = 0.0f;
int n = 0;
for(int o = 0; o < mba; o++) {
if(ba.get(i + o) < 0)
break;
tw += bw.get(i + o);
n++;
}
if(tw != 1.0f) {
for(int o = 0; o < n; o++)
bw.put(i + o, bw.get(i + o) / tw);
}
i += mba;
}
}
public void init() {}
}
}