Vegastrike 0.5.1 rc1  1.0
Original sources for Vegastrike Evolved
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
gl_light_state.cpp
Go to the documentation of this file.
1 #include <assert.h>
2 #include <cstring>
3 //#include <vegastrike.h>
4 #include "gl_globals.h"
5 #include "hashtable_3d.h"
6 #include "gl_light.h"
7 
8 #include <math.h>
9 #include "gfx/matrix.h"
10 #ifndef M_PI
11 #define M_PI 3.14159265358979323846264338328
12 #endif
13 
14 void GFXUploadLightState( int max_light_location, int active_light_array, int apparent_light_size_array, bool shader, vector<int>::const_iterator begin, vector<int>::const_iterator end )
15 {
16  // FIXME: (klauss) Very bad thing: static variables initialized with heap-allocated arrays...
17  static GLint *lightData = new GLint[GFX_MAX_LIGHTS];
18  static float *lightSizes = new float[GFX_MAX_LIGHTS*4];
19 
20  Matrix modelview;
21 
22  // if we're using shaders, we'll need the modelview matrix
23  // to properly compute light-model apparent light sizes
24  GFXGetMatrixModel(modelview);
25 
26  size_t maxval = 0;
27  size_t i = 0;
28 
29  for (vector<int>::const_iterator lightit = begin; lightit != end; ++i, ++lightit) {
30  const gfx_light &light = (*_llights)[*lightit];
31  if (light.enabled()) {
32  lightData[i] = 1;
33  maxval = i;
34 
35  // Only bother with apparent light size if there is a shader
36  if (shader) {
37  // We'll compute apparent light size by transforming the origin
38  // position by the modelview matrix, and computing the apparent
39  // size of a lightsource to that center and the proper size
40  // as entered in _gllights.
41  //
42  // This assumes the modelview matrix will be already set to
43  // the proper value when the light is enabled. It should.
44  //
45  // NOTE: We explicitly ignore rotation and scaling parts of the
46  // matrix. For one, rotation is meaningless for this calculation.
47  // For two, scaling would be nullified when scaling both distance
48  // and light size, so it would only waste time.
49 
50  QVector lightPos = light.getPosition() - modelview.p;
51 
52  double lightDistance = lightPos.Magnitude();
53  double lightSize = light.getSize() * 0.5;
54  double lightCosAngle;
55  double lightSolidAngle;
56  if (lightSize <= 0.0) {
57  // Point light always has zero solid angle
58  lightCosAngle = 1.0;
59  lightSolidAngle = 0.0;
60  } else {
61  // NOTE: assuming lightSize > 0, the following condition
62  // assures a nonzero distance to light, which would produce
63  // NaNs in the following math.
64  if (lightDistance > lightSize) {
65  // Light cos angle is:
66  // Vector(1, 0, 0) . Vector(lightDistance, lightSize, 0).Normalize()
67  // Which happens to resolve to:
68  //lightCosAngle = float(lightDistance / (lightDistance + lightSize));
69  lightCosAngle = lightDistance / sqrt( lightDistance*lightDistance + lightSize*lightSize );
70  lightSolidAngle = 2.0 * M_PI * ( 1.0 - lightCosAngle );
71  // Light steradians is:
72  // Steradians = Fractional Area * 4 * Pi
73  // Fractional Area = Apparent light area / Sky area at light distance
74  // Apparent light area = Pi * lightSize^2
75  // Sky area = 4 * Pi * lightDistance^2
76  // Do the math...
77  /*lightSolidAngle = float(M_PI
78  * ( lightSize / lightDistance )
79  * ( lightSize / lightDistance ));*/
80  } else {
81  // nil distance, avoid infinites that kill shaders
82  // NOTE: the constants aren't capricious, they're the right
83  // constants for a light coming from all around.
84  lightCosAngle = 0.0;
85  lightSolidAngle = 2.0 * M_PI;
86  }
87  }
88  lightSizes[i*4+0] = float(lightSize);
89  lightSizes[i*4+1] = float(lightCosAngle);
90  lightSizes[i*4+2] = float(lightSolidAngle);
91  lightSizes[i*4+3] = 0.f;
92  }
93  }
94  else {
95  lightData[i] = 0;
96  lightSizes[i*4+0] = 0.f;
97  lightSizes[i*4+1] = 0.f;
98  lightSizes[i*4+2] = 0.f;
99  lightSizes[i*4+3] = 0.f;
100  }
101  }
102 
103  for (; i < (size_t)GFX_MAX_LIGHTS; ++i) {
104  lightData[i] = 0;
105  lightSizes[i*4+0] = 0.f;
106  lightSizes[i*4+1] = 0.f;
107  lightSizes[i*4+2] = 0.f;
108  lightSizes[i*4+3] = 0.f;
109  }
110 
111  if (!shader) {
112  //only bother with actual GL state in the event of lack of shaders
113  for (size_t i = 0; i < (size_t) GFX_MAX_LIGHTS; ++i) {
114  int isenabled = glIsEnabled( GL_LIGHT0+i );
115  if (isenabled && !lightData[i])
116  glDisable( GL_LIGHT0+i );
117  else if (lightData[i] && !isenabled)
118  glEnable( GL_LIGHT0+i );
119  }
120  } else {
121  //only bother with shader constants it there is a shader
123  for (size_t i = 0; i <= maxval; ++i) {
124  if (lightData[i]) {
125  const gfx_light &light = (*_llights)[*(begin+i)];
126  light.ContextSwitchClobberLight(GL_LIGHT0+i, -1);
127  }
128  }
129  GFXLoadMatrixModel( modelview );
130  if (active_light_array >= 0)
131  GFXShaderConstantv( active_light_array, GFX_MAX_LIGHTS, (int*) lightData );
132  if (max_light_location >= 0)
133  GFXShaderConstanti( max_light_location, maxval );
134  if (apparent_light_size_array >= 0)
135  GFXShaderConstant4v( apparent_light_size_array, GFX_MAX_LIGHTS, (float*)lightSizes );
136  }
137 }
138 
139 #define GFX_HARDWARE_LIGHTING
140 //table to store local lights, numerical pointers to _llights (eg indices)
141 const float atten0scale = 1;
142 const float atten1scale = 1./GFX_SCALE;
143 const float atten2scale = 1./(GFX_SCALE*GFX_SCALE);
146 
148 {
149  memcpy( this, &tmp, sizeof (GFXLight) );
150  return tmp;
151 }
152 
154 {
155  int tmp = ( this-&_llights->front() );
156  assert( tmp >= 0 && tmp < (int) _llights->size() );
157  //assert (&(*_llights)[GLLights[target].index]==this);
158  return tmp;
159 } //which number it is in the main scheme of things
160 
162 {
163  int clobberdisabled = -1;
164  for (int i = 0; i < GFX_MAX_LIGHTS; i++) {
165  if (GLLights[i].index == -1)
166  return i;
167  if ( !(GLLights[i].options&OpenGLL::GLL_ON) )
168  clobberdisabled = i;
169  }
170  return clobberdisabled;
171 }
172 
174 {
175  //searches through the GLlights and sees which one is clobberable. Returns -1 if not.
176  int clobberdisabled = -1;
177  int clobberlocal = -1;
178  for (int i = 0; i < GFX_MAX_LIGHTS; i++) {
179  if (GLLights[i].index == -1)
180  return i;
181  if (GLLights[i].options&OpenGLL::GLL_LOCAL)
182  clobberlocal = i;
183  if ( !(GLLights[i].options&OpenGLL::GLL_ON) )
184  if (clobberlocal == i || clobberdisabled == -1)
185  clobberdisabled = i;
186  }
187  return (clobberdisabled == -1) ? clobberlocal : clobberdisabled;
188 }
189 
190 bool gfx_light::Create( const GFXLight &temp, bool global )
191 {
192  int foundclobberable = 0;
193  *this = temp;
194  if (!global) {
196  if ( enabled() ) {
197  disable();
198  this->Enable(); //upon creation need to call enable script with disabled light
199  }
200  } else {
201  options &= (~GFX_LOCAL_LIGHT);
202  foundclobberable = enabled() ? findGlobalClobberable() : findLocalClobberable();
203  if (foundclobberable != -1) {
204  _GLLightsEnabled += (enabled() != 0);
205  ClobberGLLight( foundclobberable );
206  }
207  }
208  return (foundclobberable != -1) || ( !enabled() );
209 }
210 
212 {
213  Disable(); //first disables it...which _will_ remove it from the light table.
214  if (target >= 0)
215  TrashFromGLLights(); //then if not already done, trash from GLlights;
216  target = -2;
217  options = 0;
218 }
219 
225 void gfx_light::SendGLPosition( const GLenum target ) const
226 {
227  float v[4] = {vect[0], vect[1], vect[2], 1};
228  glLightfv( target, GL_POSITION, v );
229 }
230 
231 inline void gfx_light::ContextSwitchClobberLight( const GLenum gltarg, const int original ) const
232 {
233  glLightf( gltarg, GL_CONSTANT_ATTENUATION, attenuate[0]*atten0scale );
234  glLightf( gltarg, GL_LINEAR_ATTENUATION, attenuate[1]*atten1scale );
235  glLightf( gltarg, GL_QUADRATIC_ATTENUATION, attenuate[2]*atten2scale );
236 
237  SendGLPosition( gltarg );
238  glLightfv( gltarg, GL_DIFFUSE, diffuse );
239  glLightfv( gltarg, GL_SPECULAR, specular );
240  glLightfv( gltarg, GL_AMBIENT, ambient );
241  if (original != -1) {
242  gfx_light *orig = &( (*_llights)[GLLights[original].index] );
243  orig->target = -1;
244  GLLights[original].index = -1;
245  }
246 }
247 
248 inline void gfx_light::FinesseClobberLight( const GLenum gltarg, const int original )
249 {
250  gfx_light *orig = &( (*_llights)[GLLights[original].index] );
251  if ( attenuated() ) {
252  if ( orig->attenuated() ) {
253  if (orig->attenuate[0] != attenuate[0])
254  glLightf( gltarg, GL_CONSTANT_ATTENUATION, attenuate[0]*atten0scale );
255  if (orig->attenuate[1] != attenuate[1])
256  glLightf( gltarg, GL_LINEAR_ATTENUATION, attenuate[1]*atten1scale );
257  if (orig->attenuate[2] != attenuate[2])
258  glLightf( gltarg, GL_QUADRATIC_ATTENUATION, attenuate[2]*atten2scale );
259  } else {
260  glLightf( gltarg, GL_CONSTANT_ATTENUATION, attenuate[0]*atten0scale );
261  glLightf( gltarg, GL_LINEAR_ATTENUATION, attenuate[1]*atten1scale );
262  glLightf( gltarg, GL_QUADRATIC_ATTENUATION, attenuate[2]*atten2scale );
263  }
264  }
265  if ( vect[0] != orig->vect[0] || vect[1] != orig->vect[1] || vect[2] != orig->vect[2] || attenuated() != orig->attenuated() )
266  SendGLPosition( gltarg );
267  if (diffuse[0] != orig->diffuse[0] || diffuse[1] != orig->diffuse[1] || diffuse[2] != orig->diffuse[2] || diffuse[3]
268  != orig->diffuse[3])
269  glLightfv( gltarg, GL_DIFFUSE, diffuse );
270  if (specular[0] != orig->specular[0] || specular[1] != orig->specular[1] || specular[2] != orig->specular[2]
271  || specular[3] != orig->specular[3])
272  glLightfv( gltarg, GL_SPECULAR, specular );
273  if (ambient[0] != orig->ambient[0] || ambient[1] != orig->ambient[1] || ambient[2] != orig->ambient[2] || ambient[3]
274  != orig->ambient[3])
275  glLightfv( gltarg, GL_AMBIENT, ambient );
276  orig->target = -1;
277  GLLights[original].index = -1;
278 }
279 
280 void gfx_light::ClobberGLLight( const int target )
281 {
282  this->target = target;
283  if ( enabled() != ( (GLLights[target].options&OpenGLL::GL_ENABLED) != 0 ) ) {
284  if ( enabled() ) {
285  glEnable( GL_LIGHT0+target );
287  } else {
288  GLLights[target].options &= (~OpenGLL::GL_ENABLED);
289  glDisable( GL_LIGHT0+target );
290  }
291  }
292  GLLights[target].options &= (OpenGLL::GL_ENABLED); //turn off options
293 #ifdef GFX_HARDWARE_LIGHTING
294  if (GLLights[target].index == -1) {
295 #endif
296  ContextSwitchClobberLight( GL_LIGHT0+target, GLLights[target].index );
297 #ifdef GFX_HARDWARE_LIGHTING
298  } else {
299  FinesseClobberLight( GL_LIGHT0+target, GLLights[target].index );
300  }
301 #endif
302  this->target = target;
303  //VSFileSystem::Fprintf (stderr,"Target %d had light %d",target, GLLights[target].index);
305  //VSFileSystem::Fprintf (stderr," Clobbered with %d\n",lightNum());
307 }
308 
309 void gfx_light::ResetProperties( const enum LIGHT_TARGET light_targ, const GFXColor &color )
310 {
311  bool changed = false;
312  if ( LocalLight() ) {
313  GFXLight t;
314  memcpy( &t, this, sizeof (GFXLight) );
315  t.SetProperties( light_targ, color );
316  changed = RemoveFromTable( false, t );
317  memcpy( this, &t, sizeof (GFXLight) );
318  if (changed)
319  AddToTable();
320  if (target >= 0)
322  return;
323  }
324  switch (light_targ)
325  {
326  case DIFFUSE:
327  diffuse[0] = color.r;
328  diffuse[1] = color.g;
329  diffuse[2] = color.b;
330  diffuse[3] = color.a;
331  if (target < 0)
332  break;
333  glLightfv( GL_LIGHT0+target, GL_DIFFUSE, diffuse );
334  break;
335  case SPECULAR:
336  specular[0] = color.r;
337  specular[1] = color.g;
338  specular[2] = color.b;
339  specular[3] = color.a;
340  if (target < 0)
341  break;
342  glLightfv( GL_LIGHT0+target, GL_SPECULAR, specular );
343  break;
344  case AMBIENT:
345  ambient[0] = color.r;
346  ambient[1] = color.g;
347  ambient[2] = color.b;
348  ambient[3] = color.a;
349  if (target < 0)
350  break;
351  glLightfv( GL_LIGHT0+target, GL_AMBIENT, ambient );
352  break;
353  case POSITION:
354  vect[0] = color.r;
355  vect[1] = color.g;
356  vect[2] = color.b;
357  if (target < 0)
358  break;
359  SendGLPosition( GL_LIGHT0+target );
360  break;
361  default:
362  case ATTENUATE:
363  attenuate[0] = color.r;
364  attenuate[1] = color.g;
365  attenuate[2] = color.b;
367  if (target < 0)
368  break;
369  SendGLPosition( GL_LIGHT0+target );
370  glLightf( GL_LIGHT0+target, GL_CONSTANT_ATTENUATION, attenuate[0]*atten0scale );
371  glLightf( GL_LIGHT0+target, GL_LINEAR_ATTENUATION, attenuate[1]*atten1scale );
372  glLightf( GL_LIGHT0+target, GL_QUADRATIC_ATTENUATION, attenuate[2]*atten2scale );
373  break;
374  }
375 }
376 
378 {
379  assert( target >= 0 );
380  assert( (GLLights[target].options&OpenGLL::GLL_ON) == 0 ); //better be disabled so we know it's not in the table, etc
381  assert( (&(*_llights)[GLLights[target].index]) == this );
383  GLLights[target].index = -1;
385  target = -1;
386 }
387 
389 {
390  LineCollideStar tmp;
391  bool err;
392  LineCollide *coltarg = new LineCollide( CalculateBounds( err ) ); //leak??
393  if (err)
394  return;
395  tmp.lc = coltarg;
396  lighttable.Put( coltarg, tmp );
397 }
398 
399 bool gfx_light::RemoveFromTable( bool shouldremove, const GFXLight &t )
400 {
401  LineCollideStar tmp;
402  bool err;
403  LineCollide coltarg( CalculateBounds( err ) );
404  if (!shouldremove) {
405  bool err2;
406  LineCollide coltarg2( CalculateBounds( err2 ) );
407  if ( lighttable.hash_int( coltarg2.Mini.i ) == lighttable.hash_int( coltarg.Mini.i )
408  && lighttable.hash_int( coltarg2.Mini.j ) == lighttable.hash_int( coltarg.Mini.j )
409  && lighttable.hash_int( coltarg2.Mini.k ) == lighttable.hash_int( coltarg.Mini.k )
410  && lighttable.hash_int( coltarg2.Maxi.i ) == lighttable.hash_int( coltarg.Maxi.i )
411  && lighttable.hash_int( coltarg2.Maxi.j ) == lighttable.hash_int( coltarg.Maxi.j )
412  && lighttable.hash_int( coltarg2.Maxi.k ) == lighttable.hash_int( coltarg.Maxi.k ) )
413  return false;
414  }
415  if (err)
416  return false;
417  tmp.lc = &coltarg;
418  if ( lighttable.Remove( &coltarg, tmp ) ) {
419  if (tmp.lc)
420  delete tmp.lc;
421  else
422  assert( tmp.lc );
423  }
424  return true;
425 }
426 
427 //unimplemented
429 {
430  if ( !enabled() ) {
431  if ( LocalLight() ) {
432  AddToTable();
433  } else {
434  if (target == -1) {
435  int newtarg = findGlobalClobberable();
436  if (newtarg == -1)
437  return;
439  ClobberGLLight( newtarg );
440  }
441  glEnable( GL_LIGHT0+this->target );
443  }
444  enable();
445  }
446 }
447 
448 //unimplemented
450 {
451  if ( enabled() ) {
452  disable();
453  if (target >= 0) {
456  glDisable( GL_LIGHT0+this->target );
457  }
458  GLLights[this->target].options &= ( ~(OpenGLL::GL_ENABLED|OpenGLL::GLL_ON) );
459  }
460  if ( LocalLight() && enabled() ) {
461  RemoveFromTable();
462  if (target >= 0)
464  }
465  }
466 }
467 
468 //i = tot/(A+B*d+C*d*d) Ai+Bi*d+Ci*d*d = tot
469 //d= (-Bi + sqrtf (B*i*B*i - 4*Ci*(Ai-tot)))/ (2Ci)
470 //d= (-B + sqrtf (B*B + 4*C*(tot/i-A)))/ (2C)
471 
473 {
474  error = false;
475  float tot_intensity = ( (specular[0]+specular[1]+specular[2])*specular[3]
476  +(diffuse[0]+diffuse[1]+diffuse[2])*diffuse[3]
477  +(ambient[0]+ambient[1]+ambient[2])*ambient[3] )*.33;
478  //double d = (-(double)attenuate[1]+sqrt((double)attenuate[1]*(double)attenuate[1]+4*(double)attenuate[2]*(((double)tot_intensity/(double)intensity_cutoff) - (double)attenuate[0])))/(2*(double)attenuate[2]);
479  //simplistic calculation above causes floating point inaccuracies
480  double ffastmathreallysucksd;
481  double ffastmathreallysucksq;
482 
483  ffastmathreallysucksd = sqrt( tot_intensity/intensity_cutoff-ambient[0] );
484 
485  ffastmathreallysucksq = sqrt( attenuate[2]+attenuate[1] );
486  //VSFileSystem::Fprintf (stderr,"q%lf d%lf",ffastmathreallysucksq,ffastmathreallysucksd);
487  if (ffastmathreallysucksq == 0 || ffastmathreallysucksd <= 0)
488  error = true;
489  ffastmathreallysucksd /= ffastmathreallysucksq;
490 
491  QVector st( vect[0]-ffastmathreallysucksd, vect[1]-ffastmathreallysucksd, vect[2]-ffastmathreallysucksd );
492  QVector end( vect[0]+ffastmathreallysucksd, vect[1]+ffastmathreallysucksd, vect[2]+ffastmathreallysucksd );
493  LineCollide retval( NULL, LineCollide::UNIT, st, end );
494  *( (int*) (&retval.object) ) = lightNum(); //put in a lightNum
495  return retval;
496 }
497 
499 {
500  unpicklights(); //picks doubtless changed position
501  for (int i = 0; i < GFX_MAX_LIGHTS; i++) {
502  if (GLLights[i].options&OpenGLL::GL_ENABLED) {
503  if (GLLights[i].index >= 0) {
504  if ( (*_llights)[GLLights[i].index].Target() == i ) {
505  (*_llights)[GLLights[i].index].SendGLPosition( GL_LIGHT0+i ); //send position transformed by current cam matrix
506  } else {
507  unsigned int li = GLLights[i].index;
508  if ( (*_llights)[li].enabled() && ( (*_llights)[li].Target() == -1 ) ) {
509  (*_llights)[li].ClobberGLLight( i );
510  } else {
511  glDisable( GL_LIGHT0+i );
512  GLLights[i].index = -1;
513  }
514  }
515  } else {
516  glDisable( GL_LIGHT0+i );
517  GLLights[i].options &= (~OpenGLL::GL_ENABLED);
518  }
519  }
520  }
521 }
522