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>
29 #include <simgear/scene/material/mat.hxx>
33 #include "pt_lights.hxx"
36 // strobe pre-draw (we want a larger point size)
37 static int StrobePreDraw( ssgEntity *e ) {
38 glPushAttrib( GL_POINT_BIT );
40 glEnable(GL_POINT_SMOOTH);
45 // strobe post-draw (we want a larger point size)
46 static int StrobePostDraw( ssgEntity *e ) {
53 // vasi pre-draw (we want a larger point size)
54 static int VASIPreDraw( ssgEntity *e ) {
55 glPushAttrib( GL_POINT_BIT );
57 glEnable(GL_POINT_SMOOTH);
62 // vasi post-draw (we want a larger point size)
63 static int VASIPostDraw( ssgEntity *e ) {
70 // Generate a directional light
71 ssgLeaf *sgMakeDirectionalLight( sgVec3 pt, sgVec3 dir, sgVec3 up,
72 const SGMaterial *mat ) {
74 // calculate a vector perpendicular to dir and up
76 sgVectorProductVec3( perp, dir, up );
78 ssgVertexArray *vl = new ssgVertexArray( 3 );
79 ssgNormalArray *nl = new ssgNormalArray( 3 );
80 ssgColourArray *cl = new ssgColourArray( 3 );
84 sgCopyVec3( tmp3, pt );
86 sgAddVec3( tmp3, up );
88 sgAddVec3( tmp3, perp );
90 // sgSubVec3( tmp3, up );
99 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
101 sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
107 // temporarily do back face
108 sgCopyVec3( tmp3, pt );
110 sgAddVec3( tmp3, up );
112 sgAddVec3( tmp3, perp );
120 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
122 sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
127 /* ssgTexCoordArray *tl = new ssgTexCoordArray( 4 );
129 sgSetVec2( tmp2, 0.0, 0.0 );
131 sgSetVec2( tmp2, 1.0, 0.0 );
133 sgSetVec2( tmp2, 1.0, 1.0 );
135 sgSetVec2( tmp2, 0.0, 1.0 );
139 new ssgVtxTable ( GL_TRIANGLES, vl, nl, NULL, cl );
142 leaf->setState( mat->get_state() );
144 SG_LOG( SG_TERRAIN, SG_ALERT, "Warning: mat = NULL" );
151 static void calc_center_point( const point_list &nodes,
152 const int_list &pnt_i,
155 sgSetVec3( pt, nodes[pnt_i[0]][0], nodes[pnt_i[0]][1], nodes[pnt_i[0]][2] );
164 for ( unsigned int i = 0; i < pnt_i.size(); ++i ) {
165 sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1],
166 nodes[pnt_i[i]][2] );
167 if ( pt[0] < minx ) { minx = pt[0]; }
168 if ( pt[0] > maxx ) { minx = pt[0]; }
169 if ( pt[1] < miny ) { miny = pt[1]; }
170 if ( pt[1] > maxy ) { miny = pt[1]; }
171 if ( pt[2] < minz ) { minz = pt[2]; }
172 if ( pt[2] > maxz ) { minz = pt[2]; }
175 sgSetVec3( result, (minx + maxx) / 2.0, (miny + maxy) / 2.0,
176 (minz + maxz) / 2.0 );
180 static ssgTransform *gen_dir_light_group( const point_list &nodes,
181 const point_list &normals,
182 const int_list &pnt_i,
183 const int_list &nml_i,
184 const SGMaterial *mat,
185 sgVec3 up, bool vertical )
188 calc_center_point( nodes, pnt_i, center );
189 // cout << center[0] << "," << center[1] << "," << center[2] << endl;
192 // find a vector perpendicular to the normal.
195 // normal isn't vertical so we can use up as our first vector
196 sgNormalizeVec3( perp1, up );
198 // normal is vertical so we have to work a bit harder to
199 // determine our first vector
201 sgSetVec3( pt1, nodes[pnt_i[0]][0], nodes[pnt_i[0]][1],
202 nodes[pnt_i[0]][2] );
203 sgSetVec3( pt2, nodes[pnt_i[1]][0], nodes[pnt_i[1]][1],
204 nodes[pnt_i[1]][2] );
206 sgSubVec3( perp1, pt2, pt1 );
207 sgNormalizeVec3( perp1 );
210 ssgVertexArray *vl = new ssgVertexArray( 3 * pnt_i.size() );
211 ssgNormalArray *nl = new ssgNormalArray( 3 * pnt_i.size() );
212 ssgColourArray *cl = new ssgColourArray( 3 * pnt_i.size() );
216 for ( i = 0; i < pnt_i.size(); ++i ) {
217 sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1],
218 nodes[pnt_i[i]][2] );
219 sgSubVec3( pt, center );
220 sgSetVec3( normal, normals[nml_i[i]][0], normals[nml_i[i]][1],
221 normals[nml_i[i]][2] );
223 // calculate a vector perpendicular to dir and up
225 sgVectorProductVec3( perp2, normal, perp1 );
229 sgCopyVec3( tmp3, pt );
231 sgAddVec3( tmp3, perp1 );
233 sgAddVec3( tmp3, perp2 );
235 // sgSubVec3( tmp3, perp1 );
241 // nl->add( normal );
244 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
246 sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
253 new ssgVtxTable ( GL_TRIANGLES, vl, nl, NULL, cl );
256 leaf->setState( mat->get_state() );
258 SG_LOG( SG_TERRAIN, SG_ALERT, "Warning: material = NULL" );
261 // put an LOD on each lighting component
262 ssgRangeSelector *lod = new ssgRangeSelector;
263 lod->setRange( 0, SG_ZERO );
264 lod->setRange( 1, 20000 );
267 // create the transformation.
269 sgSetCoord( &coord, center[0], center[1], center[2], 0.0, 0.0, 0.0 );
270 ssgTransform *trans = new ssgTransform;
271 trans->setTransform( &coord );
272 trans->addKid( lod );
278 static ssgTransform *gen_reil_lights( const point_list &nodes,
279 const point_list &normals,
280 const int_list &pnt_i,
281 const int_list &nml_i,
282 SGMaterialLib *matlib,
286 calc_center_point( nodes, pnt_i, center );
287 // cout << center[0] << "," << center[1] << "," << center[2] << endl;
290 sgNormalizeVec3( nup, up );
292 ssgVertexArray *vl = new ssgVertexArray( 3 * pnt_i.size() );
293 ssgNormalArray *nl = new ssgNormalArray( 3 * pnt_i.size() );
294 ssgColourArray *cl = new ssgColourArray( 3 * pnt_i.size() );
298 for ( i = 0; i < pnt_i.size(); ++i ) {
299 sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1],
300 nodes[pnt_i[i]][2] );
301 sgSubVec3( pt, center );
302 sgSetVec3( normal, normals[nml_i[i]][0], normals[nml_i[i]][1],
303 normals[nml_i[i]][2] );
305 // calculate a vector perpendicular to dir and up
307 sgVectorProductVec3( perp, normal, nup );
311 sgCopyVec3( tmp3, pt );
313 sgAddVec3( tmp3, nup );
315 sgAddVec3( tmp3, perp );
317 // sgSubVec3( tmp3, nup );
323 // nl->add( normal );
326 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
328 sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
335 new ssgVtxTable ( GL_TRIANGLES, vl, nl, NULL, cl );
337 SGMaterial *mat = matlib->find( "RWY_WHITE_LIGHTS" );
340 leaf->setState( mat->get_state() );
342 SG_LOG( SG_TERRAIN, SG_ALERT,
343 "Warning: can't find material = RWY_WHITE_LIGHTS" );
346 leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
347 leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
349 ssgTimedSelector *reil = new ssgTimedSelector;
351 // need to add this twice to work around an ssg bug
352 reil->addKid( leaf );
353 reil->addKid( leaf );
355 reil->setDuration( 60 );
356 reil->setLimits( 0, 2 );
357 reil->setMode( SSG_ANIM_SHUTTLE );
358 reil->control( SSG_ANIM_START );
360 // put an LOD on each lighting component
361 ssgRangeSelector *lod = new ssgRangeSelector;
362 lod->setRange( 0, SG_ZERO );
363 lod->setRange( 1, 12000 );
366 // create the transformation.
368 sgSetCoord( &coord, center[0], center[1], center[2], 0.0, 0.0, 0.0 );
369 ssgTransform *trans = new ssgTransform;
370 trans->setTransform( &coord );
371 trans->addKid( lod );
377 static ssgTransform *gen_odals_lights( const point_list &nodes,
378 const point_list &normals,
379 const int_list &pnt_i,
380 const int_list &nml_i,
381 SGMaterialLib *matlib,
385 calc_center_point( nodes, pnt_i, center );
386 // cout << center[0] << "," << center[1] << "," << center[2] << endl;
388 ssgTimedSelector *odals = new ssgTimedSelector;
391 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
393 // we don't want directional lights here
394 SGMaterial *mat = matlib->find( "GROUND_LIGHTS" );
396 SG_LOG( SG_TERRAIN, SG_ALERT,
397 "Warning: can't material = GROUND_LIGHTS" );
400 // center line strobes
403 for ( i = (int)pnt_i.size() - 1; i >= 2; --i ) {
404 ssgVertexArray *vl = new ssgVertexArray( 1 );
405 ssgColourArray *cl = new ssgColourArray( 1 );
407 sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1],
408 nodes[pnt_i[i]][2] );
409 sgSubVec3( pt, center );
415 new ssgVtxTable ( GL_POINTS, vl, NULL, NULL, cl );
417 leaf->setState( mat->get_state() );
418 leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
419 leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
421 odals->addKid( leaf );
424 // runway end strobes
425 ssgVertexArray *vl = new ssgVertexArray( 2 );
426 ssgColourArray *cl = new ssgColourArray( 2 );
428 sgSetVec3( pt, nodes[pnt_i[0]][0], nodes[pnt_i[0]][1],
429 nodes[pnt_i[0]][2] );
430 sgSubVec3( pt, center );
434 sgSetVec3( pt, nodes[pnt_i[1]][0], nodes[pnt_i[1]][1],
435 nodes[pnt_i[1]][2] );
436 sgSubVec3( pt, center );
441 new ssgVtxTable ( GL_POINTS, vl, NULL, NULL, cl );
443 leaf->setState( mat->get_state() );
444 leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
445 leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
447 odals->addKid( leaf );
451 odals->setDuration( 10 );
452 odals->setLimits( 0, pnt_i.size() - 1 );
453 odals->setMode( SSG_ANIM_SHUTTLE );
454 odals->control( SSG_ANIM_START );
456 // put an LOD on each lighting component
457 ssgRangeSelector *lod = new ssgRangeSelector;
458 lod->setRange( 0, SG_ZERO );
459 lod->setRange( 1, 12000 );
460 lod->addKid( odals );
462 // create the transformation.
464 sgSetCoord( &coord, center[0], center[1], center[2], 0.0, 0.0, 0.0 );
465 ssgTransform *trans = new ssgTransform;
466 trans->setTransform( &coord );
467 trans->addKid( lod );
473 static ssgTransform *gen_rabbit_lights( const point_list &nodes,
474 const point_list &normals,
475 const int_list &pnt_i,
476 const int_list &nml_i,
477 SGMaterialLib *matlib,
481 calc_center_point( nodes, pnt_i, center );
482 // cout << center[0] << "," << center[1] << "," << center[2] << endl;
485 sgNormalizeVec3( nup, up );
487 ssgTimedSelector *rabbit = new ssgTimedSelector;
489 SGMaterial *mat = matlib->find( "RWY_WHITE_LIGHTS" );
491 SG_LOG( SG_TERRAIN, SG_ALERT,
492 "Warning: can't material = RWY_WHITE_LIGHTS" );
497 for ( i = (int)pnt_i.size() - 1; i >= 0; --i ) {
498 ssgVertexArray *vl = new ssgVertexArray( 3 );
499 ssgNormalArray *nl = new ssgNormalArray( 3 );
500 ssgColourArray *cl = new ssgColourArray( 3 );
502 sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1],
503 nodes[pnt_i[i]][2] );
504 sgSubVec3( pt, center );
506 sgSetVec3( normal, normals[nml_i[i]][0], normals[nml_i[i]][1],
507 normals[nml_i[i]][2] );
509 // calculate a vector perpendicular to dir and up
511 sgVectorProductVec3( perp, normal, nup );
515 sgCopyVec3( tmp3, pt );
517 sgAddVec3( tmp3, nup );
519 sgAddVec3( tmp3, perp );
521 // sgSubVec3( tmp3, nup );
527 // nl->add( normal );
530 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
532 sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
538 new ssgVtxTable ( GL_TRIANGLES, vl, nl, NULL, cl );
540 leaf->setState( mat->get_state() );
541 leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
542 leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
544 rabbit->addKid( leaf );
547 rabbit->setDuration( 10 );
548 rabbit->setLimits( 0, pnt_i.size() - 1 );
549 rabbit->setMode( SSG_ANIM_SHUTTLE );
550 rabbit->control( SSG_ANIM_START );
552 // put an LOD on each lighting component
553 ssgRangeSelector *lod = new ssgRangeSelector;
554 lod->setRange( 0, SG_ZERO );
555 lod->setRange( 1, 12000 );
556 lod->addKid( rabbit );
558 // create the transformation.
560 sgSetCoord( &coord, center[0], center[1], center[2], 0.0, 0.0, 0.0 );
561 ssgTransform *trans = new ssgTransform;
562 trans->setTransform( &coord );
563 trans->addKid( lod );
569 #if 0 // debugging infrastructure
570 // Generate a normal line
571 static ssgLeaf *gen_normal_line( SGMaterialLib *matlib,
572 sgVec3 pt, sgVec3 dir, sgVec3 up )
575 ssgVertexArray *vl = new ssgVertexArray( 3 );
576 ssgColourArray *cl = new ssgColourArray( 3 );
579 sgCopyVec3( tmp3, pt );
581 sgAddVec3( tmp3, dir );
585 sgSetVec4( color, 1.0, 0.0, 0.0, 1.0 );
590 new ssgVtxTable ( GL_LINES, vl, NULL, NULL, cl );
592 SGMaterial *mat = matlib->find( "GROUND_LIGHTS" );
593 leaf->setState( mat->get_state() );
600 ssgBranch *sgMakeDirectionalLights( const point_list &nodes,
601 const point_list &normals,
602 const int_list &pnt_i,
603 const int_list &nml_i,
604 SGMaterialLib *matlib,
605 const string &material,
609 sgSetVec3( up, dup );
612 sgNormalizeVec3( nup, up );
614 SGMaterial *mat = matlib->find( material );
616 if ( material == "RWY_REIL_LIGHTS" ) {
617 // cout << "found a reil" << endl;
618 ssgTransform *reil = gen_reil_lights( nodes, normals, pnt_i, nml_i,
621 } else if ( material == "RWY_ODALS_LIGHTS" ) {
622 // cout << "found a odals" << endl;
623 ssgTransform *odals = gen_odals_lights( nodes, normals, pnt_i, nml_i,
626 } else if ( material == "RWY_SEQUENCED_LIGHTS" ) {
627 // cout << "found a rabbit" << endl;
628 ssgTransform *rabbit = gen_rabbit_lights( nodes, normals,
632 } else if ( material == "RWY_VASI_LIGHTS" ) {
633 ssgTransform *light_group = gen_dir_light_group( nodes, normals, pnt_i,
637 // calculate the geocentric position of this vasi and use it
638 // to init the vasi structure and save it in the userdata slot
640 sgdSetVec3( pos, nodes[pnt_i[0]][0], nodes[pnt_i[0]][1],
641 nodes[pnt_i[0]][2] );
642 // dup is the double version of the "up" vector which is also
643 // the reference center point of this tile. The reference
644 // center + the coordinate of the first light gives the actual
645 // location of the first light.
646 sgdAddVec3( pos, dup );
648 // extract a pointer to the leaf node so a) we can set the
649 // phat light call back and b) we can pass this to the vasi
651 ssgRangeSelector *lod = (ssgRangeSelector *)light_group->getKid(0);
652 ssgLeaf *leaf = (ssgLeaf *)lod->getKid(0);
653 leaf->setCallback( SSG_CALLBACK_PREDRAW, VASIPreDraw );
654 leaf->setCallback( SSG_CALLBACK_POSTDRAW, VASIPostDraw );
656 SGVASIUserData *vasi = new SGVASIUserData( pos, leaf );
658 light_group->setUserData( vasi );
661 } else if ( material == "RWY_BLUE_TAXIWAY_LIGHTS" ) {
662 ssgTransform *light_group = gen_dir_light_group( nodes, normals, pnt_i,
667 ssgTransform *light_group = gen_dir_light_group( nodes, normals, pnt_i,