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 // Generate a directional light
52 ssgLeaf *sgMakeDirectionalLight( sgVec3 pt, sgVec3 dir, sgVec3 up,
53 const SGMaterial *mat ) {
55 // calculate a vector perpendicular to dir and up
57 sgVectorProductVec3( perp, dir, up );
59 ssgVertexArray *vl = new ssgVertexArray( 3 );
60 ssgNormalArray *nl = new ssgNormalArray( 3 );
61 ssgColourArray *cl = new ssgColourArray( 3 );
65 sgCopyVec3( tmp3, pt );
67 sgAddVec3( tmp3, up );
69 sgAddVec3( tmp3, perp );
71 // sgSubVec3( tmp3, up );
80 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
82 sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
88 // temporarily do back face
89 sgCopyVec3( tmp3, pt );
91 sgAddVec3( tmp3, up );
93 sgAddVec3( tmp3, perp );
101 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
103 sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
108 /* ssgTexCoordArray *tl = new ssgTexCoordArray( 4 );
110 sgSetVec2( tmp2, 0.0, 0.0 );
112 sgSetVec2( tmp2, 1.0, 0.0 );
114 sgSetVec2( tmp2, 1.0, 1.0 );
116 sgSetVec2( tmp2, 0.0, 1.0 );
120 new ssgVtxTable ( GL_TRIANGLES, vl, nl, NULL, cl );
123 leaf->setState( mat->get_state() );
125 SG_LOG( SG_TERRAIN, SG_ALERT, "Warning: mat = NULL" );
132 static void calc_center_point( const point_list &nodes,
133 const int_list &pnt_i,
136 sgSetVec3( pt, nodes[pnt_i[0]][0], nodes[pnt_i[0]][1], nodes[pnt_i[0]][2] );
145 for ( unsigned int i = 0; i < pnt_i.size(); ++i ) {
146 sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1],
147 nodes[pnt_i[i]][2] );
148 if ( pt[0] < minx ) { minx = pt[0]; }
149 if ( pt[0] > maxx ) { minx = pt[0]; }
150 if ( pt[1] < miny ) { miny = pt[1]; }
151 if ( pt[1] > maxy ) { miny = pt[1]; }
152 if ( pt[2] < minz ) { minz = pt[2]; }
153 if ( pt[2] > maxz ) { minz = pt[2]; }
156 sgSetVec3( result, (minx + maxx) / 2.0, (miny + maxy) / 2.0,
157 (minz + maxz) / 2.0 );
161 static ssgTransform *gen_dir_light_group( const point_list &nodes,
162 const point_list &normals,
163 const int_list &pnt_i,
164 const int_list &nml_i,
165 const SGMaterial *mat,
166 sgVec3 up, bool vertical )
169 calc_center_point( nodes, pnt_i, center );
170 // cout << center[0] << "," << center[1] << "," << center[2] << endl;
173 // find a vector perpendicular to the normal.
176 // normal isn't vertical so we can use up as our first vector
177 sgNormalizeVec3( perp1, up );
179 // normal is vertical so we have to work a bit harder to
180 // determine our first vector
182 sgSetVec3( pt1, nodes[pnt_i[0]][0], nodes[pnt_i[0]][1],
183 nodes[pnt_i[0]][2] );
184 sgSetVec3( pt2, nodes[pnt_i[1]][0], nodes[pnt_i[1]][1],
185 nodes[pnt_i[1]][2] );
187 sgSubVec3( perp1, pt2, pt1 );
188 sgNormalizeVec3( perp1 );
191 ssgVertexArray *vl = new ssgVertexArray( 3 * pnt_i.size() );
192 ssgNormalArray *nl = new ssgNormalArray( 3 * pnt_i.size() );
193 ssgColourArray *cl = new ssgColourArray( 3 * pnt_i.size() );
197 for ( i = 0; i < pnt_i.size(); ++i ) {
198 sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1],
199 nodes[pnt_i[i]][2] );
200 sgSubVec3( pt, center );
201 sgSetVec3( normal, normals[nml_i[i]][0], normals[nml_i[i]][1],
202 normals[nml_i[i]][2] );
204 // calculate a vector perpendicular to dir and up
206 sgVectorProductVec3( perp2, normal, perp1 );
210 sgCopyVec3( tmp3, pt );
212 sgAddVec3( tmp3, perp1 );
214 sgAddVec3( tmp3, perp2 );
216 // sgSubVec3( tmp3, perp1 );
222 // nl->add( normal );
225 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
227 sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
234 new ssgVtxTable ( GL_TRIANGLES, vl, nl, NULL, cl );
237 leaf->setState( mat->get_state() );
239 SG_LOG( SG_TERRAIN, SG_ALERT, "Warning: material = NULL" );
242 // put an LOD on each lighting component
243 ssgRangeSelector *lod = new ssgRangeSelector;
244 lod->setRange( 0, SG_ZERO );
245 lod->setRange( 1, 20000 );
248 // create the transformation.
250 sgSetCoord( &coord, center[0], center[1], center[2], 0.0, 0.0, 0.0 );
251 ssgTransform *trans = new ssgTransform;
252 trans->setTransform( &coord );
253 trans->addKid( lod );
259 static ssgTransform *gen_reil_lights( const point_list &nodes,
260 const point_list &normals,
261 const int_list &pnt_i,
262 const int_list &nml_i,
263 SGMaterialLib *matlib,
267 calc_center_point( nodes, pnt_i, center );
268 // cout << center[0] << "," << center[1] << "," << center[2] << endl;
271 sgNormalizeVec3( nup, up );
273 ssgVertexArray *vl = new ssgVertexArray( 3 * pnt_i.size() );
274 ssgNormalArray *nl = new ssgNormalArray( 3 * pnt_i.size() );
275 ssgColourArray *cl = new ssgColourArray( 3 * pnt_i.size() );
279 for ( i = 0; i < pnt_i.size(); ++i ) {
280 sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1],
281 nodes[pnt_i[i]][2] );
282 sgSubVec3( pt, center );
283 sgSetVec3( normal, normals[nml_i[i]][0], normals[nml_i[i]][1],
284 normals[nml_i[i]][2] );
286 // calculate a vector perpendicular to dir and up
288 sgVectorProductVec3( perp, normal, nup );
292 sgCopyVec3( tmp3, pt );
294 sgAddVec3( tmp3, nup );
296 sgAddVec3( tmp3, perp );
298 // sgSubVec3( tmp3, nup );
304 // nl->add( normal );
307 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
309 sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
316 new ssgVtxTable ( GL_TRIANGLES, vl, nl, NULL, cl );
318 SGMaterial *mat = matlib->find( "RWY_WHITE_LIGHTS" );
321 leaf->setState( mat->get_state() );
323 SG_LOG( SG_TERRAIN, SG_ALERT,
324 "Warning: can't find material = RWY_WHITE_LIGHTS" );
327 leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
328 leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
330 ssgTimedSelector *reil = new ssgTimedSelector;
332 // need to add this twice to work around an ssg bug
333 reil->addKid( leaf );
334 reil->addKid( leaf );
336 reil->setDuration( 60 );
337 reil->setLimits( 0, 2 );
338 reil->setMode( SSG_ANIM_SHUTTLE );
339 reil->control( SSG_ANIM_START );
341 // put an LOD on each lighting component
342 ssgRangeSelector *lod = new ssgRangeSelector;
343 lod->setRange( 0, SG_ZERO );
344 lod->setRange( 1, 12000 );
347 // create the transformation.
349 sgSetCoord( &coord, center[0], center[1], center[2], 0.0, 0.0, 0.0 );
350 ssgTransform *trans = new ssgTransform;
351 trans->setTransform( &coord );
352 trans->addKid( lod );
358 static ssgTransform *gen_odals_lights( const point_list &nodes,
359 const point_list &normals,
360 const int_list &pnt_i,
361 const int_list &nml_i,
362 SGMaterialLib *matlib,
366 calc_center_point( nodes, pnt_i, center );
367 // cout << center[0] << "," << center[1] << "," << center[2] << endl;
369 ssgTimedSelector *odals = new ssgTimedSelector;
372 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
374 // we don't want directional lights here
375 SGMaterial *mat = matlib->find( "GROUND_LIGHTS" );
377 SG_LOG( SG_TERRAIN, SG_ALERT,
378 "Warning: can't material = GROUND_LIGHTS" );
381 // center line strobes
384 for ( i = (int)pnt_i.size() - 1; i >= 2; --i ) {
385 ssgVertexArray *vl = new ssgVertexArray( 1 );
386 ssgColourArray *cl = new ssgColourArray( 1 );
388 sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1],
389 nodes[pnt_i[i]][2] );
390 sgSubVec3( pt, center );
396 new ssgVtxTable ( GL_POINTS, vl, NULL, NULL, cl );
398 leaf->setState( mat->get_state() );
399 leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
400 leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
402 odals->addKid( leaf );
405 // runway end strobes
406 ssgVertexArray *vl = new ssgVertexArray( 2 );
407 ssgColourArray *cl = new ssgColourArray( 2 );
409 sgSetVec3( pt, nodes[pnt_i[0]][0], nodes[pnt_i[0]][1],
410 nodes[pnt_i[0]][2] );
411 sgSubVec3( pt, center );
415 sgSetVec3( pt, nodes[pnt_i[1]][0], nodes[pnt_i[1]][1],
416 nodes[pnt_i[1]][2] );
417 sgSubVec3( pt, center );
422 new ssgVtxTable ( GL_POINTS, vl, NULL, NULL, cl );
424 leaf->setState( mat->get_state() );
425 leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
426 leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
428 odals->addKid( leaf );
432 odals->setDuration( 10 );
433 odals->setLimits( 0, pnt_i.size() - 1 );
434 odals->setMode( SSG_ANIM_SHUTTLE );
435 odals->control( SSG_ANIM_START );
437 // put an LOD on each lighting component
438 ssgRangeSelector *lod = new ssgRangeSelector;
439 lod->setRange( 0, SG_ZERO );
440 lod->setRange( 1, 12000 );
441 lod->addKid( odals );
443 // create the transformation.
445 sgSetCoord( &coord, center[0], center[1], center[2], 0.0, 0.0, 0.0 );
446 ssgTransform *trans = new ssgTransform;
447 trans->setTransform( &coord );
448 trans->addKid( lod );
454 static ssgTransform *gen_rabbit_lights( const point_list &nodes,
455 const point_list &normals,
456 const int_list &pnt_i,
457 const int_list &nml_i,
458 SGMaterialLib *matlib,
462 calc_center_point( nodes, pnt_i, center );
463 // cout << center[0] << "," << center[1] << "," << center[2] << endl;
466 sgNormalizeVec3( nup, up );
468 ssgTimedSelector *rabbit = new ssgTimedSelector;
470 SGMaterial *mat = matlib->find( "RWY_WHITE_LIGHTS" );
472 SG_LOG( SG_TERRAIN, SG_ALERT,
473 "Warning: can't material = RWY_WHITE_LIGHTS" );
478 for ( i = (int)pnt_i.size() - 1; i >= 0; --i ) {
479 ssgVertexArray *vl = new ssgVertexArray( 3 );
480 ssgNormalArray *nl = new ssgNormalArray( 3 );
481 ssgColourArray *cl = new ssgColourArray( 3 );
483 sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1],
484 nodes[pnt_i[i]][2] );
485 sgSubVec3( pt, center );
487 sgSetVec3( normal, normals[nml_i[i]][0], normals[nml_i[i]][1],
488 normals[nml_i[i]][2] );
490 // calculate a vector perpendicular to dir and up
492 sgVectorProductVec3( perp, normal, nup );
496 sgCopyVec3( tmp3, pt );
498 sgAddVec3( tmp3, nup );
500 sgAddVec3( tmp3, perp );
502 // sgSubVec3( tmp3, nup );
508 // nl->add( normal );
511 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
513 sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
519 new ssgVtxTable ( GL_TRIANGLES, vl, nl, NULL, cl );
521 leaf->setState( mat->get_state() );
522 leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
523 leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
525 rabbit->addKid( leaf );
528 rabbit->setDuration( 10 );
529 rabbit->setLimits( 0, pnt_i.size() - 1 );
530 rabbit->setMode( SSG_ANIM_SHUTTLE );
531 rabbit->control( SSG_ANIM_START );
533 // put an LOD on each lighting component
534 ssgRangeSelector *lod = new ssgRangeSelector;
535 lod->setRange( 0, SG_ZERO );
536 lod->setRange( 1, 12000 );
537 lod->addKid( rabbit );
539 // create the transformation.
541 sgSetCoord( &coord, center[0], center[1], center[2], 0.0, 0.0, 0.0 );
542 ssgTransform *trans = new ssgTransform;
543 trans->setTransform( &coord );
544 trans->addKid( lod );
550 #if 0 // debugging infrastructure
551 // Generate a normal line
552 static ssgLeaf *gen_normal_line( SGMaterialLib *matlib,
553 sgVec3 pt, sgVec3 dir, sgVec3 up )
556 ssgVertexArray *vl = new ssgVertexArray( 3 );
557 ssgColourArray *cl = new ssgColourArray( 3 );
560 sgCopyVec3( tmp3, pt );
562 sgAddVec3( tmp3, dir );
566 sgSetVec4( color, 1.0, 0.0, 0.0, 1.0 );
571 new ssgVtxTable ( GL_LINES, vl, NULL, NULL, cl );
573 SGMaterial *mat = matlib->find( "GROUND_LIGHTS" );
574 leaf->setState( mat->get_state() );
581 ssgBranch *sgMakeDirectionalLights( const point_list &nodes,
582 const point_list &normals,
583 const int_list &pnt_i,
584 const int_list &nml_i,
585 SGMaterialLib *matlib,
586 const string &material,
590 sgSetVec3( up, dup );
593 sgNormalizeVec3( nup, up );
595 SGMaterial *mat = matlib->find( material );
597 if ( material == "RWY_REIL_LIGHTS" ) {
598 // cout << "found a reil" << endl;
599 ssgTransform *reil = gen_reil_lights( nodes, normals, pnt_i, nml_i,
602 } else if ( material == "RWY_ODALS_LIGHTS" ) {
603 // cout << "found a odals" << endl;
604 ssgTransform *odals = gen_odals_lights( nodes, normals, pnt_i, nml_i,
607 } else if ( material == "RWY_SEQUENCED_LIGHTS" ) {
608 // cout << "found a rabbit" << endl;
609 ssgTransform *rabbit = gen_rabbit_lights( nodes, normals,
613 } else if ( material == "RWY_VASI_LIGHTS" ) {
614 ssgTransform *light_group = gen_dir_light_group( nodes, normals, pnt_i,
618 // calculate the geocentric position of this vasi and use it
619 // to init the vasi structure and save it in the userdata slot
621 sgdSetVec3( pos, nodes[pnt_i[0]][0], nodes[pnt_i[0]][1],
622 nodes[pnt_i[0]][2] );
623 // dup is the double version of the "up" vector which is also
624 // the reference center point of this tile. The reference
625 // center + the coordinate of the first light gives the actual
626 // location of the first light.
627 sgdAddVec3( pos, dup );
629 // extract a pointer to the leaf node so a) we can set the
630 // phat light call back and b) we can pass this to the vasi
632 ssgRangeSelector *lod = (ssgRangeSelector *)light_group->getKid(0);
633 ssgLeaf *leaf = (ssgLeaf *)lod->getKid(0);
634 leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
635 leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
637 SGVASIUserData *vasi = new SGVASIUserData( pos, leaf );
639 light_group->setUserData( vasi );
642 } else if ( material == "RWY_BLUE_TAXIWAY_LIGHTS" ) {
643 ssgTransform *light_group = gen_dir_light_group( nodes, normals, pnt_i,
648 ssgTransform *light_group = gen_dir_light_group( nodes, normals, pnt_i,