Merge branch 'master' of ssh://repo.or.cz/srv/git/tgl
[tgl.git] / src / light.c
blobd5eb4857e2039889cd7ca8978ca4204e06c187db
1 #include "zgl.h"
2 #include "msghandling.h"
4 void glopMaterial(GLContext *c,GLParam *p)
6 int mode=p[1].i;
7 int type=p[2].i;
8 float *v=&p[3].f;
9 int i;
10 GLMaterial *m;
12 if (mode == GL_FRONT_AND_BACK) {
13 p[1].i=GL_FRONT;
14 glopMaterial(c,p);
15 mode=GL_BACK;
17 if (mode == GL_FRONT) m=&c->materials[0];
18 else m=&c->materials[1];
20 switch(type) {
21 case GL_EMISSION:
22 for(i=0;i<4;i++)
23 m->emission.v[i]=v[i];
24 break;
25 case GL_AMBIENT:
26 for(i=0;i<4;i++)
27 m->ambient.v[i]=v[i];
28 break;
29 case GL_DIFFUSE:
30 for(i=0;i<4;i++)
31 m->diffuse.v[i]=v[i];
32 break;
33 case GL_SPECULAR:
34 for(i=0;i<4;i++)
35 m->specular.v[i]=v[i];
36 break;
37 case GL_SHININESS:
38 m->shininess=v[0];
39 m->shininess_i = (v[0]/128.0f)*SPECULAR_BUFFER_RESOLUTION;
40 break;
41 case GL_AMBIENT_AND_DIFFUSE:
42 for(i=0;i<4;i++)
43 m->diffuse.v[i]=v[i];
44 for(i=0;i<4;i++)
45 m->ambient.v[i]=v[i];
46 break;
47 default:
48 assert(0);
52 void glopColorMaterial(GLContext *c,GLParam *p)
54 int mode=p[1].i;
55 int type=p[2].i;
57 c->current_color_material_mode=mode;
58 c->current_color_material_type=type;
61 void glopLight(GLContext *c,GLParam *p)
63 int light=p[1].i;
64 int type=p[2].i;
65 V4 v;
66 GLLight *l;
67 int i;
69 assert(light >= GL_LIGHT0 && light < GL_LIGHT0+MAX_LIGHTS );
71 l=&c->lights[light-GL_LIGHT0];
73 for(i=0;i<4;i++) v.v[i]=p[3+i].f;
75 switch(type) {
76 case GL_AMBIENT:
77 l->ambient=v;
78 break;
79 case GL_DIFFUSE:
80 l->diffuse=v;
81 break;
82 case GL_SPECULAR:
83 l->specular=v;
84 break;
85 case GL_POSITION:
87 V4 pos;
88 gl_M4_MulV4(&pos,c->matrix_stack_ptr[0],&v);
90 l->position=pos;
92 if (l->position.v[3] == 0) {
93 l->norm_position.X=pos.X;
94 l->norm_position.Y=pos.Y;
95 l->norm_position.Z=pos.Z;
97 gl_V3_Norm(&l->norm_position);
100 break;
101 case GL_SPOT_DIRECTION:
102 for(i=0;i<3;i++) {
103 l->spot_direction.v[i]=v.v[i];
104 l->norm_spot_direction.v[i]=v.v[i];
106 gl_V3_Norm(&l->norm_spot_direction);
107 break;
108 case GL_SPOT_EXPONENT:
109 l->spot_exponent=v.v[0];
110 break;
111 case GL_SPOT_CUTOFF:
113 float a=v.v[0];
114 assert(a == 180 || (a>=0 && a<=90));
115 l->spot_cutoff=a;
116 if (a != 180) l->cos_spot_cutoff=cos(a * M_PI / 180.0);
118 break;
119 case GL_CONSTANT_ATTENUATION:
120 l->attenuation[0]=v.v[0];
121 break;
122 case GL_LINEAR_ATTENUATION:
123 l->attenuation[1]=v.v[0];
124 break;
125 case GL_QUADRATIC_ATTENUATION:
126 l->attenuation[2]=v.v[0];
127 break;
128 default:
129 assert(0);
134 void glopLightModel(GLContext *c,GLParam *p)
136 int pname=p[1].i;
137 float *v=&p[2].f;
138 int i;
140 switch(pname) {
141 case GL_LIGHT_MODEL_AMBIENT:
142 for(i=0;i<4;i++)
143 c->ambient_light_model.v[i]=v[i];
144 break;
145 case GL_LIGHT_MODEL_LOCAL_VIEWER:
146 c->local_light_model=(int)v[0];
147 break;
148 case GL_LIGHT_MODEL_TWO_SIDE:
149 c->light_model_two_side = (int)v[0];
150 break;
151 default:
152 tgl_warning("glopLightModel: illegal pname: 0x%x\n", pname);
153 //assert(0);
154 break;
159 static inline float clampf(float a,float min,float max)
161 if (a<min) return min;
162 else if (a>max) return max;
163 else return a;
166 void gl_enable_disable_light(GLContext *c,int light,int v)
168 GLLight *l=&c->lights[light];
169 if (v && !l->enabled) {
170 l->enabled=1;
171 l->next=c->first_light;
172 c->first_light=l;
173 l->prev=NULL;
174 } else if (!v && l->enabled) {
175 l->enabled=0;
176 if (l->prev == NULL) c->first_light=l->next;
177 else l->prev->next=l->next;
178 if (l->next != NULL) l->next->prev=l->prev;
182 /* non optimized lightening model */
183 void gl_shade_vertex(GLContext *c,GLVertex *v)
185 float R,G,B,A;
186 GLMaterial *m;
187 GLLight *l;
188 V3 n,s,d;
189 float dist,tmp,att,dot,dot_spot,dot_spec;
190 int twoside = c->light_model_two_side;
192 m=&c->materials[0];
194 n.X=v->normal.X;
195 n.Y=v->normal.Y;
196 n.Z=v->normal.Z;
198 R=m->emission.v[0]+m->ambient.v[0]*c->ambient_light_model.v[0];
199 G=m->emission.v[1]+m->ambient.v[1]*c->ambient_light_model.v[1];
200 B=m->emission.v[2]+m->ambient.v[2]*c->ambient_light_model.v[2];
201 A=clampf(m->diffuse.v[3],0,1);
203 for(l=c->first_light;l!=NULL;l=l->next) {
204 float lR,lB,lG;
206 /* ambient */
207 lR=l->ambient.v[0] * m->ambient.v[0];
208 lG=l->ambient.v[1] * m->ambient.v[1];
209 lB=l->ambient.v[2] * m->ambient.v[2];
211 if (l->position.v[3] == 0) {
212 /* light at infinity */
213 d.X=l->position.v[0];
214 d.Y=l->position.v[1];
215 d.Z=l->position.v[2];
216 att=1;
217 } else {
218 /* distance attenuation */
219 d.X=l->position.v[0]-v->ec.v[0];
220 d.Y=l->position.v[1]-v->ec.v[1];
221 d.Z=l->position.v[2]-v->ec.v[2];
222 dist=sqrt(d.X*d.X+d.Y*d.Y+d.Z*d.Z);
223 if (dist>1E-3) {
224 tmp=1/dist;
225 d.X*=tmp;
226 d.Y*=tmp;
227 d.Z*=tmp;
229 att=1.0f/(l->attenuation[0]+dist*(l->attenuation[1]+
230 dist*l->attenuation[2]));
232 dot=d.X*n.X+d.Y*n.Y+d.Z*n.Z;
233 if (twoside && dot < 0) dot = -dot;
234 if (dot>0) {
235 /* diffuse light */
236 lR+=dot * l->diffuse.v[0] * m->diffuse.v[0];
237 lG+=dot * l->diffuse.v[1] * m->diffuse.v[1];
238 lB+=dot * l->diffuse.v[2] * m->diffuse.v[2];
240 /* spot light */
241 if (l->spot_cutoff != 180) {
242 dot_spot=-(d.X*l->norm_spot_direction.v[0]+
243 d.Y*l->norm_spot_direction.v[1]+
244 d.Z*l->norm_spot_direction.v[2]);
245 if (twoside && dot_spot < 0) dot_spot = -dot_spot;
246 if (dot_spot < l->cos_spot_cutoff) {
247 /* no contribution */
248 continue;
249 } else {
250 /* TODO: optimize */
251 if (l->spot_exponent > 0) {
252 att=att*pow(dot_spot,l->spot_exponent);
257 /* specular light */
259 if (c->local_light_model) {
260 V3 vcoord;
261 vcoord.X=v->ec.X;
262 vcoord.Y=v->ec.Y;
263 vcoord.Z=v->ec.Z;
264 gl_V3_Norm(&vcoord);
265 s.X=d.X-vcoord.X;
266 s.Y=d.Y-vcoord.X;
267 s.Z=d.Z-vcoord.X;
268 } else {
269 s.X=d.X;
270 s.Y=d.Y;
271 s.Z=d.Z+1.0;
273 dot_spec=n.X*s.X+n.Y*s.Y+n.Z*s.Z;
274 if (twoside && dot_spec < 0) dot_spec = -dot_spec;
275 if (dot_spec>0) {
276 GLSpecBuf *specbuf;
277 int idx;
278 tmp=sqrt(s.X*s.X+s.Y*s.Y+s.Z*s.Z);
279 if (tmp > 1E-3) {
280 dot_spec=dot_spec / tmp;
283 /* TODO: optimize */
284 /* testing specular buffer code */
285 /* dot_spec= pow(dot_spec,m->shininess);*/
286 specbuf = specbuf_get_buffer(c, m->shininess_i, m->shininess);
287 idx = (int)(dot_spec*SPECULAR_BUFFER_SIZE);
288 if (idx > SPECULAR_BUFFER_SIZE) idx = SPECULAR_BUFFER_SIZE;
289 dot_spec = specbuf->buf[idx];
290 lR+=dot_spec * l->specular.v[0] * m->specular.v[0];
291 lG+=dot_spec * l->specular.v[1] * m->specular.v[1];
292 lB+=dot_spec * l->specular.v[2] * m->specular.v[2];
296 R+=att * lR;
297 G+=att * lG;
298 B+=att * lB;
301 v->color.v[0]=clampf(R,0,1);
302 v->color.v[1]=clampf(G,0,1);
303 v->color.v[2]=clampf(B,0,1);
304 v->color.v[3]=A;