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 - curt@flightgear.org
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., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <simgear/scene/material/mat.hxx>
27 #include <simgear/scene/material/matlib.hxx>
31 #include "pt_lights.hxx"
34 // strobe pre-draw (we want a larger point size)
35 static int StrobePreDraw( ssgEntity *e ) {
36 glPushAttrib( GL_POINT_BIT );
38 glEnable(GL_POINT_SMOOTH);
43 // strobe post-draw (we want a larger point size)
44 static int StrobePostDraw( ssgEntity *e ) {
51 // vasi pre-draw (we want a larger point size)
52 static int VASIPreDraw( ssgEntity *e ) {
53 glPushAttrib( GL_POINT_BIT );
55 glEnable(GL_POINT_SMOOTH);
60 // vasi post-draw (we want a larger point size)
61 static int VASIPostDraw( ssgEntity *e ) {
68 // Generate a directional light
69 ssgLeaf *sgMakeDirectionalLight( sgVec3 pt, sgVec3 dir, sgVec3 up,
70 const SGMaterial *mat ) {
72 // calculate a vector perpendicular to dir and up
74 sgVectorProductVec3( perp, dir, up );
76 ssgVertexArray *vl = new ssgVertexArray( 3 );
77 ssgNormalArray *nl = new ssgNormalArray( 3 );
78 ssgColourArray *cl = new ssgColourArray( 3 );
82 sgCopyVec3( tmp3, pt );
84 sgAddVec3( tmp3, up );
86 sgAddVec3( tmp3, perp );
88 // sgSubVec3( tmp3, up );
97 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
99 sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
105 // temporarily do back face
106 sgCopyVec3( tmp3, pt );
108 sgAddVec3( tmp3, up );
110 sgAddVec3( tmp3, perp );
118 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
120 sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
125 /* ssgTexCoordArray *tl = new ssgTexCoordArray( 4 );
127 sgSetVec2( tmp2, 0.0, 0.0 );
129 sgSetVec2( tmp2, 1.0, 0.0 );
131 sgSetVec2( tmp2, 1.0, 1.0 );
133 sgSetVec2( tmp2, 0.0, 1.0 );
137 new ssgVtxTable ( GL_TRIANGLES, vl, nl, NULL, cl );
140 leaf->setState( mat->get_state() );
142 SG_LOG( SG_TERRAIN, SG_ALERT, "Warning: mat = NULL" );
149 static void calc_center_point( const point_list &nodes,
150 const int_list &pnt_i,
153 sgSetVec3( pt, nodes[pnt_i[0]][0], nodes[pnt_i[0]][1], nodes[pnt_i[0]][2] );
162 for ( unsigned int i = 0; i < pnt_i.size(); ++i ) {
163 sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1],
164 nodes[pnt_i[i]][2] );
165 if ( pt[0] < minx ) { minx = pt[0]; }
166 if ( pt[0] > maxx ) { minx = pt[0]; }
167 if ( pt[1] < miny ) { miny = pt[1]; }
168 if ( pt[1] > maxy ) { miny = pt[1]; }
169 if ( pt[2] < minz ) { minz = pt[2]; }
170 if ( pt[2] > maxz ) { minz = pt[2]; }
173 sgSetVec3( result, (minx + maxx) / 2.0, (miny + maxy) / 2.0,
174 (minz + maxz) / 2.0 );
178 static ssgTransform *gen_dir_light_group( const point_list &nodes,
179 const point_list &normals,
180 const int_list &pnt_i,
181 const int_list &nml_i,
182 const SGMaterial *mat,
183 sgVec3 up, bool vertical )
186 calc_center_point( nodes, pnt_i, center );
187 // cout << center[0] << "," << center[1] << "," << center[2] << endl;
190 // find a vector perpendicular to the normal.
193 // normal isn't vertical so we can use up as our first vector
194 sgNormalizeVec3( perp1, up );
196 // normal is vertical so we have to work a bit harder to
197 // determine our first vector
199 sgSetVec3( pt1, nodes[pnt_i[0]][0], nodes[pnt_i[0]][1],
200 nodes[pnt_i[0]][2] );
201 sgSetVec3( pt2, nodes[pnt_i[1]][0], nodes[pnt_i[1]][1],
202 nodes[pnt_i[1]][2] );
204 sgSubVec3( perp1, pt2, pt1 );
205 sgNormalizeVec3( perp1 );
208 ssgVertexArray *vl = new ssgVertexArray( 3 * pnt_i.size() );
209 ssgNormalArray *nl = new ssgNormalArray( 3 * pnt_i.size() );
210 ssgColourArray *cl = new ssgColourArray( 3 * pnt_i.size() );
214 for ( i = 0; i < pnt_i.size(); ++i ) {
215 sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1],
216 nodes[pnt_i[i]][2] );
217 sgSubVec3( pt, center );
218 sgSetVec3( normal, normals[nml_i[i]][0], normals[nml_i[i]][1],
219 normals[nml_i[i]][2] );
221 // calculate a vector perpendicular to dir and up
223 sgVectorProductVec3( perp2, normal, perp1 );
227 sgCopyVec3( tmp3, pt );
229 sgAddVec3( tmp3, perp1 );
231 sgAddVec3( tmp3, perp2 );
233 // sgSubVec3( tmp3, perp1 );
239 // nl->add( normal );
242 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
244 sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
251 new ssgVtxTable ( GL_TRIANGLES, vl, nl, NULL, cl );
254 leaf->setState( mat->get_state() );
256 SG_LOG( SG_TERRAIN, SG_ALERT, "Warning: material = NULL" );
259 // put an LOD on each lighting component
260 ssgRangeSelector *lod = new ssgRangeSelector;
261 lod->setRange( 0, SG_ZERO );
262 lod->setRange( 1, 20000 );
265 // create the transformation.
267 sgSetCoord( &coord, center[0], center[1], center[2], 0.0, 0.0, 0.0 );
268 ssgTransform *trans = new ssgTransform;
269 trans->setTransform( &coord );
270 trans->addKid( lod );
276 static ssgTransform *gen_reil_lights( const point_list &nodes,
277 const point_list &normals,
278 const int_list &pnt_i,
279 const int_list &nml_i,
280 SGMaterialLib *matlib,
284 calc_center_point( nodes, pnt_i, center );
285 // cout << center[0] << "," << center[1] << "," << center[2] << endl;
288 sgNormalizeVec3( nup, up );
290 ssgVertexArray *vl = new ssgVertexArray( 3 * pnt_i.size() );
291 ssgNormalArray *nl = new ssgNormalArray( 3 * pnt_i.size() );
292 ssgColourArray *cl = new ssgColourArray( 3 * pnt_i.size() );
296 for ( i = 0; i < pnt_i.size(); ++i ) {
297 sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1],
298 nodes[pnt_i[i]][2] );
299 sgSubVec3( pt, center );
300 sgSetVec3( normal, normals[nml_i[i]][0], normals[nml_i[i]][1],
301 normals[nml_i[i]][2] );
303 // calculate a vector perpendicular to dir and up
305 sgVectorProductVec3( perp, normal, nup );
309 sgCopyVec3( tmp3, pt );
311 sgAddVec3( tmp3, nup );
313 sgAddVec3( tmp3, perp );
315 // sgSubVec3( tmp3, nup );
321 // nl->add( normal );
324 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
326 sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
333 new ssgVtxTable ( GL_TRIANGLES, vl, nl, NULL, cl );
335 SGMaterial *mat = matlib->find( "RWY_WHITE_LIGHTS" );
338 leaf->setState( mat->get_state() );
340 SG_LOG( SG_TERRAIN, SG_ALERT,
341 "Warning: can't find material = RWY_WHITE_LIGHTS" );
344 leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
345 leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
347 ssgTimedSelector *reil = new ssgTimedSelector;
349 // need to add this twice to work around an ssg bug
350 reil->addKid( leaf );
351 reil->addKid( leaf );
353 reil->setDuration( 60 );
354 reil->setLimits( 0, 2 );
355 reil->setMode( SSG_ANIM_SHUTTLE );
356 reil->control( SSG_ANIM_START );
358 // put an LOD on each lighting component
359 ssgRangeSelector *lod = new ssgRangeSelector;
360 lod->setRange( 0, SG_ZERO );
361 lod->setRange( 1, 12000 );
364 // create the transformation.
366 sgSetCoord( &coord, center[0], center[1], center[2], 0.0, 0.0, 0.0 );
367 ssgTransform *trans = new ssgTransform;
368 trans->setTransform( &coord );
369 trans->addKid( lod );
375 static ssgTransform *gen_odals_lights( const point_list &nodes,
376 const point_list &normals,
377 const int_list &pnt_i,
378 const int_list &nml_i,
379 SGMaterialLib *matlib,
383 calc_center_point( nodes, pnt_i, center );
384 // cout << center[0] << "," << center[1] << "," << center[2] << endl;
386 ssgTimedSelector *odals = new ssgTimedSelector;
389 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
391 // we don't want directional lights here
392 SGMaterial *mat = matlib->find( "GROUND_LIGHTS" );
394 SG_LOG( SG_TERRAIN, SG_ALERT,
395 "Warning: can't material = GROUND_LIGHTS" );
398 // center line strobes
401 for ( i = (int)pnt_i.size() - 1; i >= 2; --i ) {
402 ssgVertexArray *vl = new ssgVertexArray( 1 );
403 ssgColourArray *cl = new ssgColourArray( 1 );
405 sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1],
406 nodes[pnt_i[i]][2] );
407 sgSubVec3( pt, center );
413 new ssgVtxTable ( GL_POINTS, vl, NULL, NULL, cl );
415 leaf->setState( mat->get_state() );
416 leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
417 leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
419 odals->addKid( leaf );
422 // runway end strobes
423 ssgVertexArray *vl = new ssgVertexArray( 2 );
424 ssgColourArray *cl = new ssgColourArray( 2 );
426 sgSetVec3( pt, nodes[pnt_i[0]][0], nodes[pnt_i[0]][1],
427 nodes[pnt_i[0]][2] );
428 sgSubVec3( pt, center );
432 sgSetVec3( pt, nodes[pnt_i[1]][0], nodes[pnt_i[1]][1],
433 nodes[pnt_i[1]][2] );
434 sgSubVec3( pt, center );
439 new ssgVtxTable ( GL_POINTS, vl, NULL, NULL, cl );
441 leaf->setState( mat->get_state() );
442 leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
443 leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
445 odals->addKid( leaf );
449 odals->setDuration( 10 );
450 odals->setLimits( 0, pnt_i.size() - 1 );
451 odals->setMode( SSG_ANIM_SHUTTLE );
452 odals->control( SSG_ANIM_START );
454 // put an LOD on each lighting component
455 ssgRangeSelector *lod = new ssgRangeSelector;
456 lod->setRange( 0, SG_ZERO );
457 lod->setRange( 1, 12000 );
458 lod->addKid( odals );
460 // create the transformation.
462 sgSetCoord( &coord, center[0], center[1], center[2], 0.0, 0.0, 0.0 );
463 ssgTransform *trans = new ssgTransform;
464 trans->setTransform( &coord );
465 trans->addKid( lod );
471 static ssgTransform *gen_rabbit_lights( const point_list &nodes,
472 const point_list &normals,
473 const int_list &pnt_i,
474 const int_list &nml_i,
475 SGMaterialLib *matlib,
479 calc_center_point( nodes, pnt_i, center );
480 // cout << center[0] << "," << center[1] << "," << center[2] << endl;
483 sgNormalizeVec3( nup, up );
485 ssgTimedSelector *rabbit = new ssgTimedSelector;
487 SGMaterial *mat = matlib->find( "RWY_WHITE_LIGHTS" );
489 SG_LOG( SG_TERRAIN, SG_ALERT,
490 "Warning: can't material = RWY_WHITE_LIGHTS" );
495 for ( i = (int)pnt_i.size() - 1; i >= 0; --i ) {
496 ssgVertexArray *vl = new ssgVertexArray( 3 );
497 ssgNormalArray *nl = new ssgNormalArray( 3 );
498 ssgColourArray *cl = new ssgColourArray( 3 );
500 sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1],
501 nodes[pnt_i[i]][2] );
502 sgSubVec3( pt, center );
504 sgSetVec3( normal, normals[nml_i[i]][0], normals[nml_i[i]][1],
505 normals[nml_i[i]][2] );
507 // calculate a vector perpendicular to dir and up
509 sgVectorProductVec3( perp, normal, nup );
513 sgCopyVec3( tmp3, pt );
515 sgAddVec3( tmp3, nup );
517 sgAddVec3( tmp3, perp );
519 // sgSubVec3( tmp3, nup );
525 // nl->add( normal );
528 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
530 sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
536 new ssgVtxTable ( GL_TRIANGLES, vl, nl, NULL, cl );
538 leaf->setState( mat->get_state() );
539 leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
540 leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
542 rabbit->addKid( leaf );
545 rabbit->setDuration( 10 );
546 rabbit->setLimits( 0, pnt_i.size() - 1 );
547 rabbit->setMode( SSG_ANIM_SHUTTLE );
548 rabbit->control( SSG_ANIM_START );
550 // put an LOD on each lighting component
551 ssgRangeSelector *lod = new ssgRangeSelector;
552 lod->setRange( 0, SG_ZERO );
553 lod->setRange( 1, 12000 );
554 lod->addKid( rabbit );
556 // create the transformation.
558 sgSetCoord( &coord, center[0], center[1], center[2], 0.0, 0.0, 0.0 );
559 ssgTransform *trans = new ssgTransform;
560 trans->setTransform( &coord );
561 trans->addKid( lod );
567 #if 0 // debugging infrastructure
568 // Generate a normal line
569 static ssgLeaf *gen_normal_line( SGMaterialLib *matlib,
570 sgVec3 pt, sgVec3 dir, sgVec3 up )
573 ssgVertexArray *vl = new ssgVertexArray( 3 );
574 ssgColourArray *cl = new ssgColourArray( 3 );
577 sgCopyVec3( tmp3, pt );
579 sgAddVec3( tmp3, dir );
583 sgSetVec4( color, 1.0, 0.0, 0.0, 1.0 );
588 new ssgVtxTable ( GL_LINES, vl, NULL, NULL, cl );
590 SGMaterial *mat = matlib->find( "GROUND_LIGHTS" );
591 leaf->setState( mat->get_state() );
598 ssgBranch *sgMakeDirectionalLights( const point_list &nodes,
599 const point_list &normals,
600 const int_list &pnt_i,
601 const int_list &nml_i,
602 SGMaterialLib *matlib,
603 const string &material,
607 sgSetVec3( up, dup );
610 sgNormalizeVec3( nup, up );
612 SGMaterial *mat = matlib->find( material );
614 if ( material == "RWY_REIL_LIGHTS" ) {
615 // cout << "found a reil" << endl;
616 ssgTransform *reil = gen_reil_lights( nodes, normals, pnt_i, nml_i,
619 } else if ( material == "RWY_ODALS_LIGHTS" ) {
620 // cout << "found a odals" << endl;
621 ssgTransform *odals = gen_odals_lights( nodes, normals, pnt_i, nml_i,
624 } else if ( material == "RWY_SEQUENCED_LIGHTS" ) {
625 // cout << "found a rabbit" << endl;
626 ssgTransform *rabbit = gen_rabbit_lights( nodes, normals,
630 } else if ( material == "RWY_VASI_LIGHTS" ) {
631 ssgTransform *light_group = gen_dir_light_group( nodes, normals, pnt_i,
635 // calculate the geocentric position of this vasi and use it
636 // to init the vasi structure and save it in the userdata slot
638 sgdSetVec3( pos, nodes[pnt_i[0]][0], nodes[pnt_i[0]][1],
639 nodes[pnt_i[0]][2] );
640 // dup is the double version of the "up" vector which is also
641 // the reference center point of this tile. The reference
642 // center + the coordinate of the first light gives the actual
643 // location of the first light.
644 sgdAddVec3( pos, dup );
646 // extract a pointer to the leaf node so a) we can set the
647 // phat light call back and b) we can pass this to the vasi
649 ssgRangeSelector *lod = (ssgRangeSelector *)light_group->getKid(0);
650 ssgLeaf *leaf = (ssgLeaf *)lod->getKid(0);
651 leaf->setCallback( SSG_CALLBACK_PREDRAW, VASIPreDraw );
652 leaf->setCallback( SSG_CALLBACK_POSTDRAW, VASIPostDraw );
654 SGVASIUserData *vasi = new SGVASIUserData( pos, leaf );
656 light_group->setUserData( vasi );
659 } else if ( material == "RWY_BLUE_TAXIWAY_LIGHTS" ) {
660 ssgTransform *light_group = gen_dir_light_group( nodes, normals, pnt_i,
665 ssgTransform *light_group = gen_dir_light_group( nodes, normals, pnt_i,