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>
30 #include <simgear/scene/material/matlib.hxx>
34 #include "pt_lights.hxx"
37 // strobe pre-draw (we want a larger point size)
38 static int StrobePreDraw( ssgEntity *e ) {
39 glPushAttrib( GL_POINT_BIT );
41 glEnable(GL_POINT_SMOOTH);
46 // strobe post-draw (we want a larger point size)
47 static int StrobePostDraw( ssgEntity *e ) {
54 // vasi pre-draw (we want a larger point size)
55 static int VASIPreDraw( ssgEntity *e ) {
56 glPushAttrib( GL_POINT_BIT );
58 glEnable(GL_POINT_SMOOTH);
63 // vasi post-draw (we want a larger point size)
64 static int VASIPostDraw( ssgEntity *e ) {
71 // Generate a directional light
72 ssgLeaf *sgMakeDirectionalLight( sgVec3 pt, sgVec3 dir, sgVec3 up,
73 const SGMaterial *mat ) {
75 // calculate a vector perpendicular to dir and up
77 sgVectorProductVec3( perp, dir, up );
79 ssgVertexArray *vl = new ssgVertexArray( 3 );
80 ssgNormalArray *nl = new ssgNormalArray( 3 );
81 ssgColourArray *cl = new ssgColourArray( 3 );
85 sgCopyVec3( tmp3, pt );
87 sgAddVec3( tmp3, up );
89 sgAddVec3( tmp3, perp );
91 // sgSubVec3( tmp3, up );
100 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
102 sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
108 // temporarily do back face
109 sgCopyVec3( tmp3, pt );
111 sgAddVec3( tmp3, up );
113 sgAddVec3( tmp3, perp );
121 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
123 sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
128 /* ssgTexCoordArray *tl = new ssgTexCoordArray( 4 );
130 sgSetVec2( tmp2, 0.0, 0.0 );
132 sgSetVec2( tmp2, 1.0, 0.0 );
134 sgSetVec2( tmp2, 1.0, 1.0 );
136 sgSetVec2( tmp2, 0.0, 1.0 );
140 new ssgVtxTable ( GL_TRIANGLES, vl, nl, NULL, cl );
143 leaf->setState( mat->get_state() );
145 SG_LOG( SG_TERRAIN, SG_ALERT, "Warning: mat = NULL" );
152 static void calc_center_point( const point_list &nodes,
153 const int_list &pnt_i,
156 sgSetVec3( pt, nodes[pnt_i[0]][0], nodes[pnt_i[0]][1], nodes[pnt_i[0]][2] );
165 for ( unsigned int i = 0; i < pnt_i.size(); ++i ) {
166 sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1],
167 nodes[pnt_i[i]][2] );
168 if ( pt[0] < minx ) { minx = pt[0]; }
169 if ( pt[0] > maxx ) { minx = pt[0]; }
170 if ( pt[1] < miny ) { miny = pt[1]; }
171 if ( pt[1] > maxy ) { miny = pt[1]; }
172 if ( pt[2] < minz ) { minz = pt[2]; }
173 if ( pt[2] > maxz ) { minz = pt[2]; }
176 sgSetVec3( result, (minx + maxx) / 2.0, (miny + maxy) / 2.0,
177 (minz + maxz) / 2.0 );
181 static ssgTransform *gen_dir_light_group( const point_list &nodes,
182 const point_list &normals,
183 const int_list &pnt_i,
184 const int_list &nml_i,
185 const SGMaterial *mat,
186 sgVec3 up, bool vertical )
189 calc_center_point( nodes, pnt_i, center );
190 // cout << center[0] << "," << center[1] << "," << center[2] << endl;
193 // find a vector perpendicular to the normal.
196 // normal isn't vertical so we can use up as our first vector
197 sgNormalizeVec3( perp1, up );
199 // normal is vertical so we have to work a bit harder to
200 // determine our first vector
202 sgSetVec3( pt1, nodes[pnt_i[0]][0], nodes[pnt_i[0]][1],
203 nodes[pnt_i[0]][2] );
204 sgSetVec3( pt2, nodes[pnt_i[1]][0], nodes[pnt_i[1]][1],
205 nodes[pnt_i[1]][2] );
207 sgSubVec3( perp1, pt2, pt1 );
208 sgNormalizeVec3( perp1 );
211 ssgVertexArray *vl = new ssgVertexArray( 3 * pnt_i.size() );
212 ssgNormalArray *nl = new ssgNormalArray( 3 * pnt_i.size() );
213 ssgColourArray *cl = new ssgColourArray( 3 * pnt_i.size() );
217 for ( i = 0; i < pnt_i.size(); ++i ) {
218 sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1],
219 nodes[pnt_i[i]][2] );
220 sgSubVec3( pt, center );
221 sgSetVec3( normal, normals[nml_i[i]][0], normals[nml_i[i]][1],
222 normals[nml_i[i]][2] );
224 // calculate a vector perpendicular to dir and up
226 sgVectorProductVec3( perp2, normal, perp1 );
230 sgCopyVec3( tmp3, pt );
232 sgAddVec3( tmp3, perp1 );
234 sgAddVec3( tmp3, perp2 );
236 // sgSubVec3( tmp3, perp1 );
242 // nl->add( normal );
245 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
247 sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
254 new ssgVtxTable ( GL_TRIANGLES, vl, nl, NULL, cl );
257 leaf->setState( mat->get_state() );
259 SG_LOG( SG_TERRAIN, SG_ALERT, "Warning: material = NULL" );
262 // put an LOD on each lighting component
263 ssgRangeSelector *lod = new ssgRangeSelector;
264 lod->setRange( 0, SG_ZERO );
265 lod->setRange( 1, 20000 );
268 // create the transformation.
270 sgSetCoord( &coord, center[0], center[1], center[2], 0.0, 0.0, 0.0 );
271 ssgTransform *trans = new ssgTransform;
272 trans->setTransform( &coord );
273 trans->addKid( lod );
279 static ssgTransform *gen_reil_lights( const point_list &nodes,
280 const point_list &normals,
281 const int_list &pnt_i,
282 const int_list &nml_i,
283 SGMaterialLib *matlib,
287 calc_center_point( nodes, pnt_i, center );
288 // cout << center[0] << "," << center[1] << "," << center[2] << endl;
291 sgNormalizeVec3( nup, up );
293 ssgVertexArray *vl = new ssgVertexArray( 3 * pnt_i.size() );
294 ssgNormalArray *nl = new ssgNormalArray( 3 * pnt_i.size() );
295 ssgColourArray *cl = new ssgColourArray( 3 * pnt_i.size() );
299 for ( i = 0; i < pnt_i.size(); ++i ) {
300 sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1],
301 nodes[pnt_i[i]][2] );
302 sgSubVec3( pt, center );
303 sgSetVec3( normal, normals[nml_i[i]][0], normals[nml_i[i]][1],
304 normals[nml_i[i]][2] );
306 // calculate a vector perpendicular to dir and up
308 sgVectorProductVec3( perp, normal, nup );
312 sgCopyVec3( tmp3, pt );
314 sgAddVec3( tmp3, nup );
316 sgAddVec3( tmp3, perp );
318 // sgSubVec3( tmp3, nup );
324 // nl->add( normal );
327 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
329 sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
336 new ssgVtxTable ( GL_TRIANGLES, vl, nl, NULL, cl );
338 SGMaterial *mat = matlib->find( "RWY_WHITE_LIGHTS" );
341 leaf->setState( mat->get_state() );
343 SG_LOG( SG_TERRAIN, SG_ALERT,
344 "Warning: can't find material = RWY_WHITE_LIGHTS" );
347 leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
348 leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
350 ssgTimedSelector *reil = new ssgTimedSelector;
352 // need to add this twice to work around an ssg bug
353 reil->addKid( leaf );
354 reil->addKid( leaf );
356 reil->setDuration( 60 );
357 reil->setLimits( 0, 2 );
358 reil->setMode( SSG_ANIM_SHUTTLE );
359 reil->control( SSG_ANIM_START );
361 // put an LOD on each lighting component
362 ssgRangeSelector *lod = new ssgRangeSelector;
363 lod->setRange( 0, SG_ZERO );
364 lod->setRange( 1, 12000 );
367 // create the transformation.
369 sgSetCoord( &coord, center[0], center[1], center[2], 0.0, 0.0, 0.0 );
370 ssgTransform *trans = new ssgTransform;
371 trans->setTransform( &coord );
372 trans->addKid( lod );
378 static ssgTransform *gen_odals_lights( const point_list &nodes,
379 const point_list &normals,
380 const int_list &pnt_i,
381 const int_list &nml_i,
382 SGMaterialLib *matlib,
386 calc_center_point( nodes, pnt_i, center );
387 // cout << center[0] << "," << center[1] << "," << center[2] << endl;
389 ssgTimedSelector *odals = new ssgTimedSelector;
392 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
394 // we don't want directional lights here
395 SGMaterial *mat = matlib->find( "GROUND_LIGHTS" );
397 SG_LOG( SG_TERRAIN, SG_ALERT,
398 "Warning: can't material = GROUND_LIGHTS" );
401 // center line strobes
404 for ( i = (int)pnt_i.size() - 1; i >= 2; --i ) {
405 ssgVertexArray *vl = new ssgVertexArray( 1 );
406 ssgColourArray *cl = new ssgColourArray( 1 );
408 sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1],
409 nodes[pnt_i[i]][2] );
410 sgSubVec3( pt, center );
416 new ssgVtxTable ( GL_POINTS, vl, NULL, NULL, cl );
418 leaf->setState( mat->get_state() );
419 leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
420 leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
422 odals->addKid( leaf );
425 // runway end strobes
426 ssgVertexArray *vl = new ssgVertexArray( 2 );
427 ssgColourArray *cl = new ssgColourArray( 2 );
429 sgSetVec3( pt, nodes[pnt_i[0]][0], nodes[pnt_i[0]][1],
430 nodes[pnt_i[0]][2] );
431 sgSubVec3( pt, center );
435 sgSetVec3( pt, nodes[pnt_i[1]][0], nodes[pnt_i[1]][1],
436 nodes[pnt_i[1]][2] );
437 sgSubVec3( pt, center );
442 new ssgVtxTable ( GL_POINTS, vl, NULL, NULL, cl );
444 leaf->setState( mat->get_state() );
445 leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
446 leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
448 odals->addKid( leaf );
452 odals->setDuration( 10 );
453 odals->setLimits( 0, pnt_i.size() - 1 );
454 odals->setMode( SSG_ANIM_SHUTTLE );
455 odals->control( SSG_ANIM_START );
457 // put an LOD on each lighting component
458 ssgRangeSelector *lod = new ssgRangeSelector;
459 lod->setRange( 0, SG_ZERO );
460 lod->setRange( 1, 12000 );
461 lod->addKid( odals );
463 // create the transformation.
465 sgSetCoord( &coord, center[0], center[1], center[2], 0.0, 0.0, 0.0 );
466 ssgTransform *trans = new ssgTransform;
467 trans->setTransform( &coord );
468 trans->addKid( lod );
474 static ssgTransform *gen_rabbit_lights( const point_list &nodes,
475 const point_list &normals,
476 const int_list &pnt_i,
477 const int_list &nml_i,
478 SGMaterialLib *matlib,
482 calc_center_point( nodes, pnt_i, center );
483 // cout << center[0] << "," << center[1] << "," << center[2] << endl;
486 sgNormalizeVec3( nup, up );
488 ssgTimedSelector *rabbit = new ssgTimedSelector;
490 SGMaterial *mat = matlib->find( "RWY_WHITE_LIGHTS" );
492 SG_LOG( SG_TERRAIN, SG_ALERT,
493 "Warning: can't material = RWY_WHITE_LIGHTS" );
498 for ( i = (int)pnt_i.size() - 1; i >= 0; --i ) {
499 ssgVertexArray *vl = new ssgVertexArray( 3 );
500 ssgNormalArray *nl = new ssgNormalArray( 3 );
501 ssgColourArray *cl = new ssgColourArray( 3 );
503 sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1],
504 nodes[pnt_i[i]][2] );
505 sgSubVec3( pt, center );
507 sgSetVec3( normal, normals[nml_i[i]][0], normals[nml_i[i]][1],
508 normals[nml_i[i]][2] );
510 // calculate a vector perpendicular to dir and up
512 sgVectorProductVec3( perp, normal, nup );
516 sgCopyVec3( tmp3, pt );
518 sgAddVec3( tmp3, nup );
520 sgAddVec3( tmp3, perp );
522 // sgSubVec3( tmp3, nup );
528 // nl->add( normal );
531 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
533 sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
539 new ssgVtxTable ( GL_TRIANGLES, vl, nl, NULL, cl );
541 leaf->setState( mat->get_state() );
542 leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
543 leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
545 rabbit->addKid( leaf );
548 rabbit->setDuration( 10 );
549 rabbit->setLimits( 0, pnt_i.size() - 1 );
550 rabbit->setMode( SSG_ANIM_SHUTTLE );
551 rabbit->control( SSG_ANIM_START );
553 // put an LOD on each lighting component
554 ssgRangeSelector *lod = new ssgRangeSelector;
555 lod->setRange( 0, SG_ZERO );
556 lod->setRange( 1, 12000 );
557 lod->addKid( rabbit );
559 // create the transformation.
561 sgSetCoord( &coord, center[0], center[1], center[2], 0.0, 0.0, 0.0 );
562 ssgTransform *trans = new ssgTransform;
563 trans->setTransform( &coord );
564 trans->addKid( lod );
570 #if 0 // debugging infrastructure
571 // Generate a normal line
572 static ssgLeaf *gen_normal_line( SGMaterialLib *matlib,
573 sgVec3 pt, sgVec3 dir, sgVec3 up )
576 ssgVertexArray *vl = new ssgVertexArray( 3 );
577 ssgColourArray *cl = new ssgColourArray( 3 );
580 sgCopyVec3( tmp3, pt );
582 sgAddVec3( tmp3, dir );
586 sgSetVec4( color, 1.0, 0.0, 0.0, 1.0 );
591 new ssgVtxTable ( GL_LINES, vl, NULL, NULL, cl );
593 SGMaterial *mat = matlib->find( "GROUND_LIGHTS" );
594 leaf->setState( mat->get_state() );
601 ssgBranch *sgMakeDirectionalLights( const point_list &nodes,
602 const point_list &normals,
603 const int_list &pnt_i,
604 const int_list &nml_i,
605 SGMaterialLib *matlib,
606 const string &material,
610 sgSetVec3( up, dup );
613 sgNormalizeVec3( nup, up );
615 SGMaterial *mat = matlib->find( material );
617 if ( material == "RWY_REIL_LIGHTS" ) {
618 // cout << "found a reil" << endl;
619 ssgTransform *reil = gen_reil_lights( nodes, normals, pnt_i, nml_i,
622 } else if ( material == "RWY_ODALS_LIGHTS" ) {
623 // cout << "found a odals" << endl;
624 ssgTransform *odals = gen_odals_lights( nodes, normals, pnt_i, nml_i,
627 } else if ( material == "RWY_SEQUENCED_LIGHTS" ) {
628 // cout << "found a rabbit" << endl;
629 ssgTransform *rabbit = gen_rabbit_lights( nodes, normals,
633 } else if ( material == "RWY_VASI_LIGHTS" ) {
634 ssgTransform *light_group = gen_dir_light_group( nodes, normals, pnt_i,
638 // calculate the geocentric position of this vasi and use it
639 // to init the vasi structure and save it in the userdata slot
641 sgdSetVec3( pos, nodes[pnt_i[0]][0], nodes[pnt_i[0]][1],
642 nodes[pnt_i[0]][2] );
643 // dup is the double version of the "up" vector which is also
644 // the reference center point of this tile. The reference
645 // center + the coordinate of the first light gives the actual
646 // location of the first light.
647 sgdAddVec3( pos, dup );
649 // extract a pointer to the leaf node so a) we can set the
650 // phat light call back and b) we can pass this to the vasi
652 ssgRangeSelector *lod = (ssgRangeSelector *)light_group->getKid(0);
653 ssgLeaf *leaf = (ssgLeaf *)lod->getKid(0);
654 leaf->setCallback( SSG_CALLBACK_PREDRAW, VASIPreDraw );
655 leaf->setCallback( SSG_CALLBACK_POSTDRAW, VASIPostDraw );
657 SGVASIUserData *vasi = new SGVASIUserData( pos, leaf );
659 light_group->setUserData( vasi );
662 } else if ( material == "RWY_BLUE_TAXIWAY_LIGHTS" ) {
663 ssgTransform *light_group = gen_dir_light_group( nodes, normals, pnt_i,
668 ssgTransform *light_group = gen_dir_light_group( nodes, normals, pnt_i,