X-Git-Url: http://git.tdb.fi/?p=libs%2Fgl.git;a=blobdiff_plain;f=source%2Flight.cpp;h=4ba581e029f0083068e1517b1b870b4d7b552c18;hp=2192536450fdfd3a3d69235a02f641848833b16e;hb=975162f86071985913b59a835d38046e1c271594;hpb=787e0f5daaccb912fd351b26f9cb9026719ab384 diff --git a/source/light.cpp b/source/light.cpp index 21925364..4ba581e0 100644 --- a/source/light.cpp +++ b/source/light.cpp @@ -1,56 +1,151 @@ #include +#include +#include #include "light.h" +#include "lightunit.h" +#include "matrix.h" #include "misc.h" +#include "programdata.h" using namespace std; namespace Msp { namespace GL { -unsigned Light::current_unit = 0; -vector Light::current_lights(1); - Light::Light(): diffuse(1), specular(1), position(0, 0, 1, 0), spot_dir(0, 0, -1), spot_exp(0), - spot_cutoff(180) + spot_cutoff(Geometry::Angle::straight()) { attenuation[0] = 1; attenuation[1] = 0; attenuation[2] = 0; } +Light::~Light() +{ + while(LightUnit *unit = LightUnit::find_unit(this)) + unbind_from(unit->get_index()); +} + +void Light::update_parameter(int mask, int index) const +{ + if(index<0) + { + LightUnit *unit = LightUnit::find_unit(this); + if(!unit) + return; + + index = unit->get_index(); + } + + GLenum l = GL_LIGHT0+index; + if(mask&DIFFUSE) + glLightfv(l, GL_DIFFUSE, &diffuse.r); + if(mask&SPECULAR) + glLightfv(l, GL_SPECULAR, &specular.r); + if(mask&POSITION) + glLightfv(l, GL_POSITION, &position.x); + if(mask&SPOT_DIR) + glLightfv(l, GL_SPOT_DIRECTION, &spot_dir.x); + if(mask&SPOT_EXP) + glLightf(l, GL_SPOT_EXPONENT, spot_exp); + if(mask&SPOT_CUTOFF) + glLightf(l, GL_SPOT_CUTOFF, spot_cutoff.degrees()); + if(mask&ATTENUATION) + { + glLightf(l, GL_CONSTANT_ATTENUATION, attenuation[0]); + glLightf(l, GL_LINEAR_ATTENUATION, attenuation[1]); + glLightf(l, GL_QUADRATIC_ATTENUATION, attenuation[2]); + } +} + +void Light::update_matrix() +{ + Vector3 up_dir; + if(20*abs(direction.z)>abs(direction.x)+abs(direction.y)) + up_dir.y = 1; + else + up_dir.z = 1; + Vector3 right_dir = normalize(cross(direction, up_dir)); + + Vector4 columns[4]; + columns[0] = compose(right_dir, 0.0f); + columns[1] = compose(cross(right_dir, direction), 0.0f); + columns[2] = compose(-direction, 0.0f); + columns[3] = position; + matrix = Matrix::from_columns(columns); +} + void Light::set_diffuse(const Color &c) { diffuse = c; + update_parameter(DIFFUSE); } void Light::set_specular(const Color &c) { specular = c; + update_parameter(SPECULAR); +} + +void Light::set_matrix(const Matrix &m) +{ + Placeable::set_matrix(m); + position = matrix.column(3); + spot_dir = normalize(-matrix.column(2).slice<3>(0)); + direction = (position.w ? spot_dir : normalize(-position.slice<3>(0))); + update_parameter(POSITION|SPOT_DIR); + update_matrix(); } void Light::set_position(const Vector4 &p) { position = p; + update_parameter(POSITION); + if(!position.w) + direction = normalize(-position.slice<3>(0)); + update_matrix(); } void Light::set_spot_direction(const Vector3 &d) { - spot_dir = d; + spot_dir = normalize(d); + if(position.w) + direction = spot_dir; + update_parameter(SPOT_DIR); + update_matrix(); } void Light::set_spot_exponent(float e) { + if(e<0) + throw invalid_argument("Light::set_spot_exponent"); + spot_exp = e; + update_parameter(SPOT_EXP); } void Light::set_spot_cutoff(float c) { + set_spot_cutoff(Geometry::Angle::from_degrees(c)); +} + +void Light::set_spot_cutoff(const Geometry::Angle &c) +{ + if(c::zero() || (c>Geometry::Angle::right() && c!=Geometry::Angle::straight())) + throw invalid_argument("Light::set_spot_cutoff"); + spot_cutoff = c; + update_parameter(SPOT_CUTOFF); +} + +void Light::disable_spot_cutoff() +{ + set_spot_cutoff(Geometry::Angle::straight()); } void Light::set_attenuation(float c, float l, float q) @@ -58,59 +153,87 @@ void Light::set_attenuation(float c, float l, float q) attenuation[0] = c; attenuation[1] = l; attenuation[2] = q; + update_parameter(ATTENUATION); } -void Light::bind() const +void Light::update_shader_data(ProgramData &shdata, const Matrix &view_matrix, unsigned i) const { - if(current_lights[current_unit]!=this) + string base = format("light_sources[%d]", i); + shdata.uniform(base+".position", view_matrix*position); + shdata.uniform(base+".diffuse", diffuse); + shdata.uniform(base+".specular", specular); +} + +void Light::bind_to(unsigned i) const +{ + static Require _req(MSP_legacy_features); + + LightUnit &unit = LightUnit::get_unit(i); + if(unit.set_light(this)) { - GLenum l = GL_LIGHT0+current_unit; - enable(l); - glLightfv(l, GL_DIFFUSE, &diffuse.r); - glLightfv(l, GL_SPECULAR, &specular.r); - glLightfv(l, GL_POSITION, &position.x); - glLightfv(l, GL_SPOT_DIRECTION, &spot_dir.x); - glLightf(l, GL_SPOT_EXPONENT, spot_exp); - glLightf(l, GL_SPOT_CUTOFF, spot_cutoff); - glLightf(l, GL_CONSTANT_ATTENUATION, attenuation[0]); - glLightf(l, GL_LINEAR_ATTENUATION, attenuation[1]); - glLightf(l, GL_QUADRATIC_ATTENUATION, attenuation[2]); - current_lights[current_unit] = this; + enable(GL_LIGHT0+unit.get_index()); + update_parameter(-1, unit.get_index()); } } -void Light::bind_to(unsigned i) const +const Light *Light::current(unsigned i) +{ + return LightUnit::get_unit(i).get_light(); +} + +void Light::unbind_from(unsigned i) { - activate(i); - bind(); + LightUnit &unit = LightUnit::get_unit(i); + if(unit.set_light(0)) + disable(GL_LIGHT0+unit.get_index()); } -void Light::activate(unsigned i) + +Light::Loader::Loader(Light &l): + DataFile::ObjectLoader(l) { - static unsigned max_lights = get_i(GL_MAX_LIGHTS); + add("attenuation", &Loader::attenuation); + add("diffuse", &Loader::diffuse); + add("position", &Loader::position); + add("specular", &Loader::specular); + add("spot_direction", &Loader::spot_direction); + add("spot_exponent", &Loader::spot_exponent); + add("spot_cutoff", &Loader::spot_cutoff); +} - if(i>=max_lights) - throw out_of_range("Light::activate"); +void Light::Loader::attenuation(float c, float l, float q) +{ + obj.set_attenuation(c, l, q); +} - if(i>=current_lights.size()) - current_lights.resize(i+1); +void Light::Loader::diffuse(float r, float g, float b) +{ + obj.set_diffuse(Color(r, g, b)); +} - current_unit = i; +void Light::Loader::position(float x, float y, float z, float w) +{ + obj.set_position(Vector4(x, y, z, w)); } -void Light::unbind() +void Light::Loader::specular(float r, float g, float b) { - if(current_lights[current_unit]) - { - disable(GL_LIGHT0+current_unit); - current_lights[current_unit] = 0; - } + obj.set_specular(Color(r, g, b)); } -void Light::unbind_from(unsigned i) +void Light::Loader::spot_direction(float x, float y, float z) +{ + obj.set_spot_direction(Vector3(x, y, z)); +} + +void Light::Loader::spot_exponent(float e) +{ + obj.set_spot_exponent(e); +} + +void Light::Loader::spot_cutoff(float c) { - activate(i); - unbind(); + obj.set_spot_cutoff(Geometry::Angle::from_degrees(c)); } } // namespace GL