1 // pt_lights.cxx -- build a 'directional' light on the fly
3 // Written by Curtis Olson, started March 2002.
5 // Copyright (C) 2002 Curtis L. Olson - http://www.flightgear.org/~curt
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 # include <simgear_config.h>
30 #include <simgear/scene/material/mat.hxx>
31 #include <simgear/screen/extensions.hxx>
35 #include "pt_lights.hxx"
38 // static variables for use in ssg callbacks
39 static bool extensions_checked = false;
40 static bool glPointParameterIsSupported = false;
41 static glPointParameterfProc glPointParameterfPtr;
42 static glPointParameterfvProc glPointParameterfvPtr;
43 static bool SGPointLightsUseSprites = false;
44 static bool SGPointLightsEnhancedLighting = false;
45 static bool SGPointLightsDistanceAttenuation = false;
48 // Specify the way we want to draw directional point lights (assuming the
49 // appropriate extensions are available.)
51 void sgConfigureDirectionalLights( bool use_point_sprites,
52 bool enhanced_lighting,
53 bool distance_attenuation ) {
54 SGPointLightsUseSprites = use_point_sprites;
55 SGPointLightsEnhancedLighting = enhanced_lighting;
56 SGPointLightsDistanceAttenuation = distance_attenuation;
60 // runtime check for the availability of various opengl extensions.
61 static void check_for_extensions() {
62 // get the address of our OpenGL extensions
63 if (SGIsOpenGLExtensionSupported("GL_EXT_point_parameters") ) {
64 glPointParameterIsSupported = true;
65 glPointParameterfPtr = (glPointParameterfProc)
66 SGLookupFunction("glPointParameterfEXT");
67 glPointParameterfvPtr = (glPointParameterfvProc)
68 SGLookupFunction("glPointParameterfvEXT");
69 } else if ( SGIsOpenGLExtensionSupported("GL_ARB_point_parameters") ) {
70 glPointParameterIsSupported = true;
71 glPointParameterfPtr = (glPointParameterfProc)
72 SGLookupFunction("glPointParameterfARB");
73 glPointParameterfvPtr = (glPointParameterfvProc)
74 SGLookupFunction("glPointParameterfvARB");
76 glPointParameterIsSupported = false;
81 // strobe pre-draw (we want a larger point size)
82 static int StrobePreDraw( ssgEntity *e ) {
83 // check for the availability of point parameter extension, but
85 if ( !extensions_checked ) {
86 check_for_extensions();
87 extensions_checked = true;
90 glPushAttrib( GL_POINT_BIT );
91 if ( glPointParameterIsSupported && SGPointLightsEnhancedLighting ) {
92 if ( SGPointLightsUseSprites ) {
100 glEnable(GL_POINT_SMOOTH);
105 // strobe post-draw (we want a larger point size)
106 static int StrobePostDraw( ssgEntity *e ) {
113 // vasi pre-draw (we want a larger point size)
114 static int VASIPreDraw( ssgEntity *e ) {
115 // check for the availability of point parameter extension, but
117 if ( !extensions_checked ) {
118 check_for_extensions();
119 extensions_checked = true;
122 glPushAttrib( GL_POINT_BIT );
123 if ( glPointParameterIsSupported && SGPointLightsEnhancedLighting ) {
124 if ( SGPointLightsUseSprites ) {
132 glEnable(GL_POINT_SMOOTH);
137 // vasi post-draw (we want a larger point size)
138 static int VASIPostDraw( ssgEntity *e ) {
145 // Generate a directional light
146 ssgLeaf *sgMakeDirectionalLight( sgVec3 pt, sgVec3 dir, sgVec3 up,
147 const SGMaterial *mat ) {
149 // calculate a vector perpendicular to dir and up
151 sgVectorProductVec3( perp, dir, up );
153 ssgVertexArray *vl = new ssgVertexArray( 3 );
154 ssgNormalArray *nl = new ssgNormalArray( 3 );
155 ssgColourArray *cl = new ssgColourArray( 3 );
159 sgCopyVec3( tmp3, pt );
161 sgAddVec3( tmp3, up );
163 sgAddVec3( tmp3, perp );
165 // sgSubVec3( tmp3, up );
174 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
176 sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
182 // temporarily do back face
183 sgCopyVec3( tmp3, pt );
185 sgAddVec3( tmp3, up );
187 sgAddVec3( tmp3, perp );
195 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
197 sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
202 /* ssgTexCoordArray *tl = new ssgTexCoordArray( 4 );
204 sgSetVec2( tmp2, 0.0, 0.0 );
206 sgSetVec2( tmp2, 1.0, 0.0 );
208 sgSetVec2( tmp2, 1.0, 1.0 );
210 sgSetVec2( tmp2, 0.0, 1.0 );
214 new ssgVtxTable ( GL_TRIANGLES, vl, nl, NULL, cl );
217 leaf->setState( mat->get_state() );
219 SG_LOG( SG_TERRAIN, SG_ALERT, "Warning: mat = NULL" );
226 static void calc_center_point( const point_list &nodes,
227 const int_list &pnt_i,
230 sgSetVec3( pt, nodes[pnt_i[0]][0], nodes[pnt_i[0]][1], nodes[pnt_i[0]][2] );
239 for ( unsigned int i = 0; i < pnt_i.size(); ++i ) {
240 sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1],
241 nodes[pnt_i[i]][2] );
242 if ( pt[0] < minx ) { minx = pt[0]; }
243 if ( pt[0] > maxx ) { minx = pt[0]; }
244 if ( pt[1] < miny ) { miny = pt[1]; }
245 if ( pt[1] > maxy ) { miny = pt[1]; }
246 if ( pt[2] < minz ) { minz = pt[2]; }
247 if ( pt[2] > maxz ) { minz = pt[2]; }
250 sgSetVec3( result, (minx + maxx) / 2.0, (miny + maxy) / 2.0,
251 (minz + maxz) / 2.0 );
255 static ssgTransform *gen_dir_light_group( const point_list &nodes,
256 const point_list &normals,
257 const int_list &pnt_i,
258 const int_list &nml_i,
259 const SGMaterial *mat,
260 sgVec3 up, bool vertical )
263 calc_center_point( nodes, pnt_i, center );
264 // cout << center[0] << "," << center[1] << "," << center[2] << endl;
267 // find a vector perpendicular to the normal.
270 // normal isn't vertical so we can use up as our first vector
271 sgNormalizeVec3( perp1, up );
273 // normal is vertical so we have to work a bit harder to
274 // determine our first vector
276 sgSetVec3( pt1, nodes[pnt_i[0]][0], nodes[pnt_i[0]][1],
277 nodes[pnt_i[0]][2] );
278 sgSetVec3( pt2, nodes[pnt_i[1]][0], nodes[pnt_i[1]][1],
279 nodes[pnt_i[1]][2] );
281 sgSubVec3( perp1, pt2, pt1 );
282 sgNormalizeVec3( perp1 );
285 ssgVertexArray *vl = new ssgVertexArray( 3 * pnt_i.size() );
286 ssgNormalArray *nl = new ssgNormalArray( 3 * pnt_i.size() );
287 ssgColourArray *cl = new ssgColourArray( 3 * pnt_i.size() );
291 for ( i = 0; i < pnt_i.size(); ++i ) {
292 sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1],
293 nodes[pnt_i[i]][2] );
294 sgSubVec3( pt, center );
295 sgSetVec3( normal, normals[nml_i[i]][0], normals[nml_i[i]][1],
296 normals[nml_i[i]][2] );
298 // calculate a vector perpendicular to dir and up
300 sgVectorProductVec3( perp2, normal, perp1 );
304 sgCopyVec3( tmp3, pt );
306 sgAddVec3( tmp3, perp1 );
308 sgAddVec3( tmp3, perp2 );
310 // sgSubVec3( tmp3, perp1 );
316 // nl->add( normal );
319 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
321 sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
328 new ssgVtxTable ( GL_TRIANGLES, vl, nl, NULL, cl );
331 leaf->setState( mat->get_state() );
333 SG_LOG( SG_TERRAIN, SG_ALERT, "Warning: material = NULL" );
336 // put an LOD on each lighting component
337 ssgRangeSelector *lod = new ssgRangeSelector;
338 lod->setRange( 0, SG_ZERO );
339 lod->setRange( 1, 20000 );
342 // create the transformation.
344 sgSetCoord( &coord, center[0], center[1], center[2], 0.0, 0.0, 0.0 );
345 ssgTransform *trans = new ssgTransform;
346 trans->setTransform( &coord );
347 trans->addKid( lod );
353 static ssgTransform *gen_reil_lights( const point_list &nodes,
354 const point_list &normals,
355 const int_list &pnt_i,
356 const int_list &nml_i,
357 SGMaterialLib *matlib,
361 calc_center_point( nodes, pnt_i, center );
362 // cout << center[0] << "," << center[1] << "," << center[2] << endl;
365 sgNormalizeVec3( nup, up );
367 ssgVertexArray *vl = new ssgVertexArray( 3 * pnt_i.size() );
368 ssgNormalArray *nl = new ssgNormalArray( 3 * pnt_i.size() );
369 ssgColourArray *cl = new ssgColourArray( 3 * pnt_i.size() );
373 for ( i = 0; i < pnt_i.size(); ++i ) {
374 sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1],
375 nodes[pnt_i[i]][2] );
376 sgSubVec3( pt, center );
377 sgSetVec3( normal, normals[nml_i[i]][0], normals[nml_i[i]][1],
378 normals[nml_i[i]][2] );
380 // calculate a vector perpendicular to dir and up
382 sgVectorProductVec3( perp, normal, nup );
386 sgCopyVec3( tmp3, pt );
388 sgAddVec3( tmp3, nup );
390 sgAddVec3( tmp3, perp );
392 // sgSubVec3( tmp3, nup );
398 // nl->add( normal );
401 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
403 sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
410 new ssgVtxTable ( GL_TRIANGLES, vl, nl, NULL, cl );
412 SGMaterial *mat = matlib->find( "RWY_WHITE_LIGHTS" );
415 leaf->setState( mat->get_state() );
417 SG_LOG( SG_TERRAIN, SG_ALERT,
418 "Warning: can't find material = RWY_WHITE_LIGHTS" );
421 leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
422 leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
424 ssgTimedSelector *reil = new ssgTimedSelector;
426 // need to add this twice to work around an ssg bug
427 reil->addKid( leaf );
428 reil->addKid( leaf );
430 reil->setDuration( 60 );
431 reil->setLimits( 0, 2 );
432 reil->setMode( SSG_ANIM_SHUTTLE );
433 reil->control( SSG_ANIM_START );
435 // put an LOD on each lighting component
436 ssgRangeSelector *lod = new ssgRangeSelector;
437 lod->setRange( 0, SG_ZERO );
438 lod->setRange( 1, 12000 );
441 // create the transformation.
443 sgSetCoord( &coord, center[0], center[1], center[2], 0.0, 0.0, 0.0 );
444 ssgTransform *trans = new ssgTransform;
445 trans->setTransform( &coord );
446 trans->addKid( lod );
452 static ssgTransform *gen_odals_lights( const point_list &nodes,
453 const point_list &normals,
454 const int_list &pnt_i,
455 const int_list &nml_i,
456 SGMaterialLib *matlib,
460 calc_center_point( nodes, pnt_i, center );
461 // cout << center[0] << "," << center[1] << "," << center[2] << endl;
463 ssgTimedSelector *odals = new ssgTimedSelector;
466 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
468 // we don't want directional lights here
469 SGMaterial *mat = matlib->find( "GROUND_LIGHTS" );
471 SG_LOG( SG_TERRAIN, SG_ALERT,
472 "Warning: can't material = GROUND_LIGHTS" );
475 // center line strobes
478 for ( i = (int)pnt_i.size() - 1; i >= 2; --i ) {
479 ssgVertexArray *vl = new ssgVertexArray( 1 );
480 ssgColourArray *cl = new ssgColourArray( 1 );
482 sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1],
483 nodes[pnt_i[i]][2] );
484 sgSubVec3( pt, center );
490 new ssgVtxTable ( GL_POINTS, vl, NULL, NULL, cl );
492 leaf->setState( mat->get_state() );
493 leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
494 leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
496 odals->addKid( leaf );
499 // runway end strobes
500 ssgVertexArray *vl = new ssgVertexArray( 2 );
501 ssgColourArray *cl = new ssgColourArray( 2 );
503 sgSetVec3( pt, nodes[pnt_i[0]][0], nodes[pnt_i[0]][1],
504 nodes[pnt_i[0]][2] );
505 sgSubVec3( pt, center );
509 sgSetVec3( pt, nodes[pnt_i[1]][0], nodes[pnt_i[1]][1],
510 nodes[pnt_i[1]][2] );
511 sgSubVec3( pt, center );
516 new ssgVtxTable ( GL_POINTS, vl, NULL, NULL, cl );
518 leaf->setState( mat->get_state() );
519 leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
520 leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
522 odals->addKid( leaf );
526 odals->setDuration( 10 );
527 odals->setLimits( 0, pnt_i.size() - 1 );
528 odals->setMode( SSG_ANIM_SHUTTLE );
529 odals->control( SSG_ANIM_START );
531 // put an LOD on each lighting component
532 ssgRangeSelector *lod = new ssgRangeSelector;
533 lod->setRange( 0, SG_ZERO );
534 lod->setRange( 1, 12000 );
535 lod->addKid( odals );
537 // create the transformation.
539 sgSetCoord( &coord, center[0], center[1], center[2], 0.0, 0.0, 0.0 );
540 ssgTransform *trans = new ssgTransform;
541 trans->setTransform( &coord );
542 trans->addKid( lod );
548 static ssgTransform *gen_rabbit_lights( const point_list &nodes,
549 const point_list &normals,
550 const int_list &pnt_i,
551 const int_list &nml_i,
552 SGMaterialLib *matlib,
556 calc_center_point( nodes, pnt_i, center );
557 // cout << center[0] << "," << center[1] << "," << center[2] << endl;
560 sgNormalizeVec3( nup, up );
562 ssgTimedSelector *rabbit = new ssgTimedSelector;
564 SGMaterial *mat = matlib->find( "RWY_WHITE_LIGHTS" );
566 SG_LOG( SG_TERRAIN, SG_ALERT,
567 "Warning: can't material = RWY_WHITE_LIGHTS" );
572 for ( i = (int)pnt_i.size() - 1; i >= 0; --i ) {
573 ssgVertexArray *vl = new ssgVertexArray( 3 );
574 ssgNormalArray *nl = new ssgNormalArray( 3 );
575 ssgColourArray *cl = new ssgColourArray( 3 );
577 sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1],
578 nodes[pnt_i[i]][2] );
579 sgSubVec3( pt, center );
581 sgSetVec3( normal, normals[nml_i[i]][0], normals[nml_i[i]][1],
582 normals[nml_i[i]][2] );
584 // calculate a vector perpendicular to dir and up
586 sgVectorProductVec3( perp, normal, nup );
590 sgCopyVec3( tmp3, pt );
592 sgAddVec3( tmp3, nup );
594 sgAddVec3( tmp3, perp );
596 // sgSubVec3( tmp3, nup );
602 // nl->add( normal );
605 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
607 sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
613 new ssgVtxTable ( GL_TRIANGLES, vl, nl, NULL, cl );
615 leaf->setState( mat->get_state() );
616 leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
617 leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
619 rabbit->addKid( leaf );
622 rabbit->setDuration( 10 );
623 rabbit->setLimits( 0, pnt_i.size() - 1 );
624 rabbit->setMode( SSG_ANIM_SHUTTLE );
625 rabbit->control( SSG_ANIM_START );
627 // put an LOD on each lighting component
628 ssgRangeSelector *lod = new ssgRangeSelector;
629 lod->setRange( 0, SG_ZERO );
630 lod->setRange( 1, 12000 );
631 lod->addKid( rabbit );
633 // create the transformation.
635 sgSetCoord( &coord, center[0], center[1], center[2], 0.0, 0.0, 0.0 );
636 ssgTransform *trans = new ssgTransform;
637 trans->setTransform( &coord );
638 trans->addKid( lod );
644 #if 0 // debugging infrastructure
645 // Generate a normal line
646 static ssgLeaf *gen_normal_line( SGMaterialLib *matlib,
647 sgVec3 pt, sgVec3 dir, sgVec3 up )
650 ssgVertexArray *vl = new ssgVertexArray( 3 );
651 ssgColourArray *cl = new ssgColourArray( 3 );
654 sgCopyVec3( tmp3, pt );
656 sgAddVec3( tmp3, dir );
660 sgSetVec4( color, 1.0, 0.0, 0.0, 1.0 );
665 new ssgVtxTable ( GL_LINES, vl, NULL, NULL, cl );
667 SGMaterial *mat = matlib->find( "GROUND_LIGHTS" );
668 leaf->setState( mat->get_state() );
675 ssgBranch *sgMakeDirectionalLights( const point_list &nodes,
676 const point_list &normals,
677 const int_list &pnt_i,
678 const int_list &nml_i,
679 SGMaterialLib *matlib,
680 const string &material,
684 sgSetVec3( up, dup );
687 sgNormalizeVec3( nup, up );
689 SGMaterial *mat = matlib->find( material );
691 if ( material == "RWY_REIL_LIGHTS" ) {
692 // cout << "found a reil" << endl;
693 ssgTransform *reil = gen_reil_lights( nodes, normals, pnt_i, nml_i,
696 } else if ( material == "RWY_ODALS_LIGHTS" ) {
697 // cout << "found a odals" << endl;
698 ssgTransform *odals = gen_odals_lights( nodes, normals, pnt_i, nml_i,
701 } else if ( material == "RWY_SEQUENCED_LIGHTS" ) {
702 // cout << "found a rabbit" << endl;
703 ssgTransform *rabbit = gen_rabbit_lights( nodes, normals,
707 } else if ( material == "RWY_VASI_LIGHTS" ) {
708 ssgTransform *light_group = gen_dir_light_group( nodes, normals, pnt_i,
712 // calculate the geocentric position of this vasi and use it
713 // to init the vasi structure and save it in the userdata slot
715 sgdSetVec3( pos, nodes[pnt_i[0]][0], nodes[pnt_i[0]][1],
716 nodes[pnt_i[0]][2] );
717 // dup is the double version of the "up" vector which is also
718 // the reference center point of this tile. The reference
719 // center + the coordinate of the first light gives the actual
720 // location of the first light.
721 sgdAddVec3( pos, dup );
723 // extract a pointer to the leaf node so a) we can set the
724 // phat light call back and b) we can pass this to the vasi
726 ssgRangeSelector *lod = (ssgRangeSelector *)light_group->getKid(0);
727 ssgLeaf *leaf = (ssgLeaf *)lod->getKid(0);
728 leaf->setCallback( SSG_CALLBACK_PREDRAW, VASIPreDraw );
729 leaf->setCallback( SSG_CALLBACK_POSTDRAW, VASIPostDraw );
731 SGVASIUserData *vasi = new SGVASIUserData( pos, leaf );
733 light_group->setUserData( vasi );
736 } else if ( material == "RWY_BLUE_TAXIWAY_LIGHTS" ) {
737 ssgTransform *light_group = gen_dir_light_group( nodes, normals, pnt_i,
742 ssgTransform *light_group = gen_dir_light_group( nodes, normals, pnt_i,