/*
* 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 haven.glsl.*;
import java.util.*;
import java.awt.Color;
import javax.media.opengl.*;
import static haven.Utils.c2fa;
public class Light implements Rendered {
public float[] amb, dif, spc;
private static final float[] defamb = {0.0f, 0.0f, 0.0f, 1.0f};
private static final float[] defdif = {1.0f, 1.0f, 1.0f, 1.0f};
private static final float[] defspc = {1.0f, 1.0f, 1.0f, 1.0f};
public Light() {
this.amb = defamb;
this.dif = defdif;
this.spc = defspc;
}
public Light(FColor col) {
this.amb = defamb;
this.dif = this.spc = col.to4a();
}
public Light(Color col) {
this.amb = defamb;
this.dif = this.spc = c2fa(col);
}
public Light(FColor amb, FColor dif, FColor spc) {
this.amb = amb.to4a();
this.dif = dif.to4a();
this.spc = spc.to4a();
}
public Light(Color amb, Color dif, Color spc) {
this.amb = c2fa(amb);
this.dif = c2fa(dif);
this.spc = c2fa(spc);
}
public void enable(GOut g, int idx) {
GL2 gl = g.gl;
gl.glEnable(GL2.GL_LIGHT0 + idx);
gl.glLightfv(GL2.GL_LIGHT0 + idx, GL2.GL_AMBIENT, amb, 0);
gl.glLightfv(GL2.GL_LIGHT0 + idx, GL2.GL_DIFFUSE, dif, 0);
gl.glLightfv(GL2.GL_LIGHT0 + idx, GL2.GL_SPECULAR, spc, 0);
}
public void disable(GOut g, int idx) {
GL2 gl = g.gl;
gl.glLightfv(GL2.GL_LIGHT0 + idx, GL2.GL_AMBIENT, defamb, 0);
gl.glLightfv(GL2.GL_LIGHT0 + idx, GL2.GL_DIFFUSE, defdif, 0);
gl.glLightfv(GL2.GL_LIGHT0 + idx, GL2.GL_SPECULAR, defspc, 0);
gl.glDisable(GL2.GL_LIGHT0 + idx);
}
public static final GLState.Slot<LightList> lights = new GLState.Slot<LightList>(GLState.Slot.Type.DRAW, LightList.class, PView.cam);
public static final GLState.Slot<Model> model = new GLState.Slot<Model>(GLState.Slot.Type.DRAW, Model.class, PView.proj);
public static final GLState.Slot<GLState> lighting = new GLState.Slot<GLState>(GLState.Slot.Type.DRAW, GLState.class, model, lights);
public static class BaseLights extends GLState {
private final ShaderMacro[] shaders;
public BaseLights(ShaderMacro[] shaders) {
this.shaders = shaders;
}
public void apply(GOut g) {
GL2 gl = g.gl;
if(g.st.prog == null)
gl.glEnable(GL2.GL_LIGHTING);
else
reapply(g);
}
public void reapply(GOut g) {
GL2 gl = g.gl;
gl.glUniform1i(g.st.prog.uniform(Phong.nlights), g.st.get(lights).nlights);
}
public void unapply(GOut g) {
GL2 gl = g.gl;
if(!g.st.usedprog)
gl.glDisable(GL2.GL_LIGHTING);
}
public ShaderMacro[] shaders() {
return(shaders);
}
public boolean reqshaders() {
return(true);
}
public void prep(Buffer buf) {
buf.put(lighting, this);
}
}
private static final ShaderMacro vlight = new ShaderMacro() {
public void modify(ProgramContext prog) {
new Phong(prog.vctx);
}
};
private static final ShaderMacro plight = new ShaderMacro() {
public void modify(ProgramContext prog) {
new Phong(prog.fctx);
}
};
public static final GLState vlights = new BaseLights(new ShaderMacro[] {vlight}) {
public boolean reqshaders() {
return(false);
}
};
public static final GLState plights = new BaseLights(new ShaderMacro[] {plight});
public static final GLState.StandAlone celshade = new GLState.StandAlone(GLState.Slot.Type.DRAW, lighting) {
public void apply(GOut g) {}
public void unapply(GOut g) {}
private final ShaderMacro[] shaders = {new Phong.CelShade()};
public ShaderMacro[] shaders() {
return(shaders);
}
public boolean reqshaders() {
return(true);
}
};
@Material.ResName("cel")
public static class $cel implements Material.ResCons {
public GLState cons(Resource res, Object... args) {
return(celshade);
}
}
public static final GLState deflight = new GLState() {
public void apply(GOut g) {}
public void unapply(GOut g) {}
public void prep(Buffer buf) {
if(buf.cfg.pref.flight.val)
plights.prep(buf);
else
vlights.prep(buf);
if(buf.cfg.pref.cel.val)
celshade.prep(buf);
}
};
@Material.ResName("light")
public static class $light implements Material.ResCons {
public GLState cons(Resource res, Object... args) {
String nm = (String)args[0];
if(nm.equals("def")) {
return(deflight);
} else if(nm.equals("pv")) {
return(vlights);
} else if(nm.equals("pp")) {
return(plights);
} else if(nm.equals("n")) {
return(null);
} else {
throw(new Resource.LoadException("Unknown lighting type: " + nm, res));
}
}
}
public static class LightList extends GLState {
public final List<Light> ll = new ArrayList<Light>();
public final List<Matrix4f> vl = new ArrayList<Matrix4f>();
private final List<Light> en = new ArrayList<Light>();
public int nlights = 0;
public void apply(GOut g) {
GL2 gl = g.gl;
int nl = ll.size();
if(g.gc.maxlights < nl)
nl = g.gc.maxlights;
en.clear();
for(int i = 0; i < nl; i++) {
Matrix4f mv = vl.get(i);
Light l = ll.get(i);
g.st.matmode(GL2.GL_MODELVIEW);
gl.glLoadMatrixf(mv.m, 0);
en.add(l);
l.enable(g, i);
GOut.checkerr(gl);
}
nlights = nl;
}
public void unapply(GOut g) {
for(int i = 0; i < en.size(); i++) {
en.get(i).disable(g, i);
GOut.checkerr(g.gl);
}
nlights = 0;
}
public int capply() {
return(1000);
}
public int cunapply() {
return(1000);
}
public void prep(Buffer buf) {
buf.put(lights, this);
}
private void add(Light l, Matrix4f loc) {
ll.add(l);
vl.add(loc);
if(ll.size() != vl.size())
throw(new RuntimeException());
}
public int index(Light l) {
return(ll.indexOf(l));
}
}
public static class Model extends GLState {
public float[] amb;
public int cc = GL2.GL_SINGLE_COLOR;
private static final float[] defamb = {0.2f, 0.2f, 0.2f, 1.0f};
public Model(Color amb) {
this.amb = c2fa(amb);
}
public Model() {
this(Color.BLACK);
}
public void apply(GOut g) {
GL2 gl = g.gl;
gl.glLightModelfv(GL2.GL_LIGHT_MODEL_AMBIENT, amb, 0);
gl.glLightModeli(GL2.GL_LIGHT_MODEL_COLOR_CONTROL, cc);
}
public void unapply(GOut g) {
GL2 gl = g.gl;
gl.glLightModelfv(GL2.GL_LIGHT_MODEL_AMBIENT, defamb, 0);
gl.glLightModeli(GL2.GL_LIGHT_MODEL_COLOR_CONTROL, GL2.GL_SINGLE_COLOR);
}
public void prep(Buffer buf) {
buf.put(model, this);
}
}
public void draw(GOut g) {}
public boolean setup(RenderList rl) {
LightList l = rl.state().get(lights);
if(l != null) {
Camera cam = rl.state().get(PView.cam);
Location.Chain loc = rl.state().get(PView.loc);
Matrix4f mv = cam.fin(Matrix4f.identity());
if(loc != null)
mv = mv.mul(loc.fin(Matrix4f.identity()));
l.add(this, mv);
}
return(false);
}
@Resource.LayerName("light")
public static class Res extends Resource.Layer {
public final int id;
public final Color amb, dif, spc;
public boolean hatt, hexp;
public float ac, al, aq, exp;
public Coord3f dir;
private static Color cold(byte[] buf, int[] off) {
double r, g, b, a;
r = Utils.floatd(buf, off[0]); off[0] += 5;
g = Utils.floatd(buf, off[0]); off[0] += 5;
b = Utils.floatd(buf, off[0]); off[0] += 5;
a = Utils.floatd(buf, off[0]); off[0] += 5;
return(new Color((int)(r * 255.0), (int)(g * 255.0), (int)(b * 255.0), (int)(a * 255.0)));
}
public Res(Resource res, byte[] buf) {
res.super();
int[] off = {0};
this.id = Utils.int16d(buf, off[0]); off[0] += 2;
this.amb = cold(buf, off);
this.dif = cold(buf, off);
this.spc = cold(buf, off);
while(off[0] < buf.length) {
int t = buf[off[0]]; off[0]++;
if(t == 1) {
hatt = true;
ac = (float)Utils.floatd(buf, off[0]); off[0] += 5;
al = (float)Utils.floatd(buf, off[0]); off[0] += 5;
aq = (float)Utils.floatd(buf, off[0]); off[0] += 5;
} else if(t == 2) {
float x = (float)Utils.floatd(buf, off[0]); off[0] += 5;
float y = (float)Utils.floatd(buf, off[0]); off[0] += 5;
float z = (float)Utils.floatd(buf, off[0]); off[0] += 5;
dir = new Coord3f(x, y, z);
} else if(t == 3) {
hexp = true;
exp = (float)Utils.floatd(buf, off[0]); off[0] += 5;
} else {
throw(new Resource.LoadException("Unknown light data: " + t, getres()));
}
}
}
public Light make() {
if(hatt) {
PosLight ret;
if(hexp)
ret = new SpotLight(amb, dif, spc, Coord3f.o, dir, exp);
else
ret = new PosLight(amb, dif, spc, Coord3f.o);
ret.att(ac, al, aq);
return(ret);
} else {
return(new DirLight(amb, dif, spc, dir));
}
}
public void init() {
}
}
}