]> git.tdb.fi Git - libs/gl.git/blob - source/light.cpp
Fixes and improvements to exporting materials
[libs/gl.git] / source / light.cpp
1 #include <stdexcept>
2 #include <msp/gl/extensions/msp_legacy_features.h>
3 #include <msp/strings/format.h>
4 #include "light.h"
5 #include "lightunit.h"
6 #include "matrix.h"
7 #include "misc.h"
8 #include "programdata.h"
9
10 using namespace std;
11
12 namespace Msp {
13 namespace GL {
14
15 Light::Light():
16         diffuse(1),
17         specular(1),
18         position(0, 0, 1, 0),
19         spot_dir(0, 0, -1),
20         spot_exp(0),
21         spot_cutoff(Geometry::Angle<float>::straight())
22 {
23         attenuation[0] = 1;
24         attenuation[1] = 0;
25         attenuation[2] = 0;
26 }
27
28 Light::~Light()
29 {
30         while(LightUnit *unit = LightUnit::find_unit(this))
31                 unbind_from(unit->get_index());
32 }
33
34 void Light::update_parameter(int mask, int index) const
35 {
36         if(index<0)
37         {
38                 LightUnit *unit = LightUnit::find_unit(this);
39                 if(!unit)
40                         return;
41
42                 index = unit->get_index();
43         }
44
45         GLenum l = GL_LIGHT0+index;
46         if(mask&DIFFUSE)
47                 glLightfv(l, GL_DIFFUSE, &diffuse.r);
48         if(mask&SPECULAR)
49                 glLightfv(l, GL_SPECULAR, &specular.r);
50         if(mask&POSITION)
51                 glLightfv(l, GL_POSITION, &position.x);
52         if(mask&SPOT_DIR)
53                 glLightfv(l, GL_SPOT_DIRECTION, &spot_dir.x);
54         if(mask&SPOT_EXP)
55                 glLightf(l, GL_SPOT_EXPONENT, spot_exp);
56         if(mask&SPOT_CUTOFF)
57                 glLightf(l, GL_SPOT_CUTOFF, spot_cutoff.degrees());
58         if(mask&ATTENUATION)
59         {
60                 glLightf(l, GL_CONSTANT_ATTENUATION, attenuation[0]);
61                 glLightf(l, GL_LINEAR_ATTENUATION, attenuation[1]);
62                 glLightf(l, GL_QUADRATIC_ATTENUATION, attenuation[2]);
63         }
64 }
65
66 void Light::update_matrix()
67 {
68         Vector3 up_dir;
69         if(20*abs(direction.z)>abs(direction.x)+abs(direction.y))
70                 up_dir.y = 1;
71         else
72                 up_dir.z = 1;
73         Vector3 right_dir = normalize(cross(direction, up_dir));
74
75         Vector4 columns[4];
76         columns[0] = compose(right_dir, 0.0f);
77         columns[1] = compose(cross(right_dir, direction), 0.0f);
78         columns[2] = compose(-direction, 0.0f);
79         columns[3] = position;
80         matrix = Matrix::from_columns(columns);
81 }
82
83 void Light::set_diffuse(const Color &c)
84 {
85         diffuse = c;
86         update_parameter(DIFFUSE);
87 }
88
89 void Light::set_specular(const Color &c)
90 {
91         specular = c;
92         update_parameter(SPECULAR);
93 }
94
95 void Light::set_matrix(const Matrix &m)
96 {
97         Placeable::set_matrix(m);
98         position = matrix.column(3);
99         spot_dir = normalize(-matrix.column(2).slice<3>(0));
100         direction = (position.w ? spot_dir : normalize(-position.slice<3>(0)));
101         update_parameter(POSITION|SPOT_DIR);
102         update_matrix();
103 }
104
105 void Light::set_position(const Vector4 &p)
106 {
107         position = p;
108         update_parameter(POSITION);
109         if(!position.w)
110                 direction = normalize(-position.slice<3>(0));
111         update_matrix();
112 }
113
114 void Light::set_spot_direction(const Vector3 &d)
115 {
116         spot_dir = normalize(d);
117         if(position.w)
118                 direction = spot_dir;
119         update_parameter(SPOT_DIR);
120         update_matrix();
121 }
122
123 void Light::set_spot_exponent(float e)
124 {
125         if(e<0)
126                 throw invalid_argument("Light::set_spot_exponent");
127
128         spot_exp = e;
129         update_parameter(SPOT_EXP);
130 }
131
132 void Light::set_spot_cutoff(float c)
133 {
134         set_spot_cutoff(Geometry::Angle<float>::from_degrees(c));
135 }
136
137 void Light::set_spot_cutoff(const Geometry::Angle<float> &c)
138 {
139         if(c<Geometry::Angle<float>::zero() || (c>Geometry::Angle<float>::right() && c!=Geometry::Angle<float>::straight()))
140                 throw invalid_argument("Light::set_spot_cutoff");
141
142         spot_cutoff = c;
143         update_parameter(SPOT_CUTOFF);
144 }
145
146 void Light::disable_spot_cutoff()
147 {
148         set_spot_cutoff(Geometry::Angle<float>::straight());
149 }
150
151 void Light::set_attenuation(float c, float l, float q)
152 {
153         attenuation[0] = c;
154         attenuation[1] = l;
155         attenuation[2] = q;
156         update_parameter(ATTENUATION);
157 }
158
159 void Light::update_shader_data(ProgramData &shdata, const Matrix &view_matrix, unsigned i) const
160 {
161         string base = format("light_sources[%d]", i);
162         shdata.uniform(base+".position", view_matrix*position);
163         shdata.uniform(base+".diffuse", diffuse);
164         shdata.uniform(base+".specular", specular);
165 }
166
167 void Light::bind_to(unsigned i) const
168 {
169         static Require _req(MSP_legacy_features);
170
171         LightUnit &unit = LightUnit::get_unit(i);
172         if(unit.set_light(this))
173         {
174                 enable(GL_LIGHT0+unit.get_index());
175                 update_parameter(-1, unit.get_index());
176         }
177 }
178
179 const Light *Light::current(unsigned i)
180 {
181         return LightUnit::get_unit(i).get_light();
182 }
183
184 void Light::unbind_from(unsigned i)
185 {
186         LightUnit &unit = LightUnit::get_unit(i);
187         if(unit.set_light(0))
188                 disable(GL_LIGHT0+unit.get_index());
189 }
190
191
192 Light::Loader::Loader(Light &l):
193         DataFile::ObjectLoader<Light>(l)
194 {
195         add("attenuation", &Loader::attenuation);
196         add("diffuse", &Loader::diffuse);
197         add("position", &Loader::position);
198         add("specular", &Loader::specular);
199         add("spot_direction", &Loader::spot_direction);
200         add("spot_exponent", &Loader::spot_exponent);
201         add("spot_cutoff", &Loader::spot_cutoff);
202 }
203
204 void Light::Loader::attenuation(float c, float l, float q)
205 {
206         obj.set_attenuation(c, l, q);
207 }
208
209 void Light::Loader::diffuse(float r, float g, float b)
210 {
211         obj.set_diffuse(Color(r, g, b));
212 }
213
214 void Light::Loader::position(float x, float y, float z, float w)
215 {
216         obj.set_position(Vector4(x, y, z, w));
217 }
218
219 void Light::Loader::specular(float r, float g, float b)
220 {
221         obj.set_specular(Color(r, g, b));
222 }
223
224 void Light::Loader::spot_direction(float x, float y, float z)
225 {
226         obj.set_spot_direction(Vector3(x, y, z));
227 }
228
229 void Light::Loader::spot_exponent(float e)
230 {
231         obj.set_spot_exponent(e);
232 }
233
234 void Light::Loader::spot_cutoff(float c)
235 {
236         obj.set_spot_cutoff(Geometry::Angle<float>::from_degrees(c));
237 }
238
239 } // namespace GL
240 } // namespace Msp