]> git.mxchange.org Git - simgear.git/blob - simgear/scene/tgdb/pt_lights.cxx
My old email address is no longer valid ... point to my web page.
[simgear.git] / simgear / scene / tgdb / pt_lights.cxx
1 // pt_lights.cxx -- build a 'directional' light on the fly
2 //
3 // Written by Curtis Olson, started March 2002.
4 //
5 // Copyright (C) 2002  Curtis L. Olson  - http://www.flightgear.org/~curt
6 //
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.
11 //
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.
16 //
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.
20 //
21 // $Id$
22
23
24 #include <plib/sg.h>
25
26 #include <simgear/scene/material/mat.hxx>
27 #include <simgear/scene/material/matlib.hxx>
28
29 #include "vasi.hxx"
30
31 #include "pt_lights.hxx"
32
33
34 // strobe pre-draw (we want a larger point size)
35 static int StrobePreDraw( ssgEntity *e ) {
36     glPushAttrib( GL_POINT_BIT );
37     glPointSize(4.0);
38     glEnable(GL_POINT_SMOOTH);
39
40     return true;
41 }
42
43 // strobe post-draw (we want a larger point size)
44 static int StrobePostDraw( ssgEntity *e ) {
45     glPopAttrib();
46
47     return true;
48 }
49
50
51 // vasi pre-draw (we want a larger point size)
52 static int VASIPreDraw( ssgEntity *e ) {
53     glPushAttrib( GL_POINT_BIT );
54     glPointSize(2.0);
55     glEnable(GL_POINT_SMOOTH);
56
57     return true;
58 }
59
60 // vasi post-draw (we want a larger point size)
61 static int VASIPostDraw( ssgEntity *e ) {
62     glPopAttrib();
63
64     return true;
65 }
66
67
68 // Generate a directional light
69 ssgLeaf *sgMakeDirectionalLight( sgVec3 pt, sgVec3 dir, sgVec3 up, 
70                                  const SGMaterial *mat ) {
71
72     // calculate a vector perpendicular to dir and up
73     sgVec3 perp;
74     sgVectorProductVec3( perp, dir, up );
75
76     ssgVertexArray   *vl = new ssgVertexArray( 3 );
77     ssgNormalArray   *nl = new ssgNormalArray( 3 );
78     ssgColourArray   *cl = new ssgColourArray( 3 );
79
80     // front face
81     sgVec3 tmp3;
82     sgCopyVec3( tmp3, pt );
83     vl->add( tmp3 );
84     sgAddVec3( tmp3, up );
85     vl->add( tmp3 );
86     sgAddVec3( tmp3, perp );
87     vl->add( tmp3 );
88     // sgSubVec3( tmp3, up );
89     // vl->add( tmp3 );
90
91     nl->add( dir );
92     nl->add( dir );
93     nl->add( dir );
94     // nl->add( dir );
95
96     sgVec4 color;
97     sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
98     cl->add( color );
99     sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
100     cl->add( color );
101     cl->add( color );
102     // cl->add( color );
103
104     /*
105     // temporarily do back face
106     sgCopyVec3( tmp3, pt );
107     vl->add( tmp3 );
108     sgAddVec3( tmp3, up );
109     vl->add( tmp3 );
110     sgAddVec3( tmp3, perp );
111     vl->add( tmp3 );
112
113     sgNegateVec3( dir );
114     nl->add( dir );
115     nl->add( dir );
116     nl->add( dir );
117
118     sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
119     cl->add( color );
120     sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
121     cl->add( color );
122     cl->add( color );
123     */
124
125     /* ssgTexCoordArray *tl = new ssgTexCoordArray( 4 );
126     sgVec2 tmp2;
127     sgSetVec2( tmp2, 0.0, 0.0 );
128     tl->add( tmp2 );
129     sgSetVec2( tmp2, 1.0, 0.0 );
130     tl->add( tmp2 );
131     sgSetVec2( tmp2, 1.0, 1.0 );
132     tl->add( tmp2 );
133     sgSetVec2( tmp2, 0.0, 1.0 );
134     tl->add( tmp2 ); */
135
136     ssgLeaf *leaf = 
137         new ssgVtxTable ( GL_TRIANGLES, vl, nl, NULL, cl );
138
139     if ( mat != NULL ) {
140         leaf->setState( mat->get_state() );
141     } else {
142         SG_LOG( SG_TERRAIN, SG_ALERT, "Warning: mat = NULL" );
143     }
144
145     return leaf;
146 }
147
148
149 static void calc_center_point( const point_list &nodes,
150                                const int_list &pnt_i,
151                                sgVec3 result ) {
152     sgVec3 pt;
153     sgSetVec3( pt, nodes[pnt_i[0]][0], nodes[pnt_i[0]][1], nodes[pnt_i[0]][2] );
154
155     double minx = pt[0];
156     double maxx = pt[0];
157     double miny = pt[1];
158     double maxy = pt[1];
159     double minz = pt[2];
160     double maxz = pt[2];
161
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]; }
171     }
172
173     sgSetVec3( result, (minx + maxx) / 2.0, (miny + maxy) / 2.0,
174                (minz + maxz) / 2.0 );
175 }
176
177
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 )
184 {
185     sgVec3 center;
186     calc_center_point( nodes, pnt_i, center );
187     // cout << center[0] << "," << center[1] << "," << center[2] << endl;
188
189
190     // find a vector perpendicular to the normal.
191     sgVec3 perp1;
192     if ( !vertical ) {
193         // normal isn't vertical so we can use up as our first vector
194         sgNormalizeVec3( perp1, up );
195     } else {
196         // normal is vertical so we have to work a bit harder to
197         // determine our first vector
198         sgVec3 pt1, pt2;
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] );
203
204         sgSubVec3( perp1, pt2, pt1 );
205         sgNormalizeVec3( perp1 );
206     }
207
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() );
211
212     unsigned int i;
213     sgVec3 pt, normal;
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] );
220
221         // calculate a vector perpendicular to dir and up
222         sgVec3 perp2;
223         sgVectorProductVec3( perp2, normal, perp1 );
224
225         // front face
226         sgVec3 tmp3;
227         sgCopyVec3( tmp3, pt );
228         vl->add( tmp3 );
229         sgAddVec3( tmp3, perp1 );
230         vl->add( tmp3 );
231         sgAddVec3( tmp3, perp2 );
232         vl->add( tmp3 );
233         // sgSubVec3( tmp3, perp1 );
234         // vl->add( tmp3 );
235
236         nl->add( normal );
237         nl->add( normal );
238         nl->add( normal );
239         // nl->add( normal );
240
241         sgVec4 color;
242         sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
243         cl->add( color );
244         sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
245         cl->add( color );
246         cl->add( color );
247         // cl->add( color );
248     }
249
250     ssgLeaf *leaf = 
251         new ssgVtxTable ( GL_TRIANGLES, vl, nl, NULL, cl );
252
253     if ( mat != NULL ) {
254         leaf->setState( mat->get_state() );
255     } else {
256         SG_LOG( SG_TERRAIN, SG_ALERT, "Warning: material = NULL" );
257     }
258
259     // put an LOD on each lighting component
260     ssgRangeSelector *lod = new ssgRangeSelector;
261     lod->setRange( 0, SG_ZERO );
262     lod->setRange( 1, 20000 );
263     lod->addKid( leaf );
264
265     // create the transformation.
266     sgCoord coord;
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 );
271
272     return trans;
273 }
274
275
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,
281                                       sgVec3 up )
282 {
283     sgVec3 center;
284     calc_center_point( nodes, pnt_i, center );
285     // cout << center[0] << "," << center[1] << "," << center[2] << endl;
286
287     sgVec3 nup;
288     sgNormalizeVec3( nup, up );
289
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() );
293
294     unsigned int i;
295     sgVec3 pt, normal;
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] );
302
303         // calculate a vector perpendicular to dir and up
304         sgVec3 perp;
305         sgVectorProductVec3( perp, normal, nup );
306
307         // front face
308         sgVec3 tmp3;
309         sgCopyVec3( tmp3, pt );
310         vl->add( tmp3 );
311         sgAddVec3( tmp3, nup );
312         vl->add( tmp3 );
313         sgAddVec3( tmp3, perp );
314         vl->add( tmp3 );
315         // sgSubVec3( tmp3, nup );
316         // vl->add( tmp3 );
317
318         nl->add( normal );
319         nl->add( normal );
320         nl->add( normal );
321         // nl->add( normal );
322
323         sgVec4 color;
324         sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
325         cl->add( color );
326         sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
327         cl->add( color );
328         cl->add( color );
329         // cl->add( color );
330     }
331
332     ssgLeaf *leaf = 
333         new ssgVtxTable ( GL_TRIANGLES, vl, nl, NULL, cl );
334
335     SGMaterial *mat = matlib->find( "RWY_WHITE_LIGHTS" );
336
337     if ( mat != NULL ) {
338         leaf->setState( mat->get_state() );
339     } else {
340         SG_LOG( SG_TERRAIN, SG_ALERT,
341                 "Warning: can't find material = RWY_WHITE_LIGHTS" );
342     }
343
344     leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
345     leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
346
347     ssgTimedSelector *reil = new ssgTimedSelector;
348
349     // need to add this twice to work around an ssg bug
350     reil->addKid( leaf );
351     reil->addKid( leaf );
352
353     reil->setDuration( 60 );
354     reil->setLimits( 0, 2 );
355     reil->setMode( SSG_ANIM_SHUTTLE );
356     reil->control( SSG_ANIM_START );
357    
358     // put an LOD on each lighting component
359     ssgRangeSelector *lod = new ssgRangeSelector;
360     lod->setRange( 0, SG_ZERO );
361     lod->setRange( 1, 12000 );
362     lod->addKid( reil );
363
364     // create the transformation.
365     sgCoord coord;
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 );
370
371     return trans;
372 }
373
374
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,
380                                        sgVec3 up )
381 {
382     sgVec3 center;
383     calc_center_point( nodes, pnt_i, center );
384     // cout << center[0] << "," << center[1] << "," << center[2] << endl;
385
386     ssgTimedSelector *odals = new ssgTimedSelector;
387
388     sgVec4 color;
389     sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
390
391     // we don't want directional lights here
392     SGMaterial *mat = matlib->find( "GROUND_LIGHTS" );
393     if ( mat == NULL ) {
394         SG_LOG( SG_TERRAIN, SG_ALERT,
395                 "Warning: can't material = GROUND_LIGHTS" );
396     }
397
398     // center line strobes
399     int i;
400     sgVec3 pt;
401     for ( i = (int)pnt_i.size() - 1; i >= 2; --i ) {
402         ssgVertexArray   *vl = new ssgVertexArray( 1 );
403         ssgColourArray   *cl = new ssgColourArray( 1 );
404      
405         sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1],
406                    nodes[pnt_i[i]][2] );
407         sgSubVec3( pt, center );
408         vl->add( pt );
409
410         cl->add( color );
411
412         ssgLeaf *leaf = 
413             new ssgVtxTable ( GL_POINTS, vl, NULL, NULL, cl );
414
415         leaf->setState( mat->get_state() );
416         leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
417         leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
418
419         odals->addKid( leaf );
420     }
421
422     // runway end strobes
423     ssgVertexArray   *vl = new ssgVertexArray( 2 );
424     ssgColourArray   *cl = new ssgColourArray( 2 );
425      
426     sgSetVec3( pt, nodes[pnt_i[0]][0], nodes[pnt_i[0]][1],
427                nodes[pnt_i[0]][2] );
428     sgSubVec3( pt, center );
429     vl->add( pt );
430     cl->add( color );
431
432     sgSetVec3( pt, nodes[pnt_i[1]][0], nodes[pnt_i[1]][1],
433                nodes[pnt_i[1]][2] );
434     sgSubVec3( pt, center );
435     vl->add( pt );
436     cl->add( color );
437
438     ssgLeaf *leaf = 
439         new ssgVtxTable ( GL_POINTS, vl, NULL, NULL, cl );
440
441     leaf->setState( mat->get_state() );
442     leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
443     leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
444
445     odals->addKid( leaf );
446
447     // setup animition
448
449     odals->setDuration( 10 );
450     odals->setLimits( 0, pnt_i.size() - 1 );
451     odals->setMode( SSG_ANIM_SHUTTLE );
452     odals->control( SSG_ANIM_START );
453    
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 );
459
460     // create the transformation.
461     sgCoord coord;
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 );
466
467     return trans;
468 }
469
470
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,
476                                         sgVec3 up )
477 {
478     sgVec3 center;
479     calc_center_point( nodes, pnt_i, center );
480     // cout << center[0] << "," << center[1] << "," << center[2] << endl;
481
482     sgVec3 nup;
483     sgNormalizeVec3( nup, up );
484
485     ssgTimedSelector *rabbit = new ssgTimedSelector;
486
487     SGMaterial *mat = matlib->find( "RWY_WHITE_LIGHTS" );
488     if ( mat == NULL ) {
489         SG_LOG( SG_TERRAIN, SG_ALERT,
490                 "Warning: can't material = RWY_WHITE_LIGHTS" );
491     }
492
493     int i;
494     sgVec3 pt, normal;
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 );
499      
500         sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1],
501                    nodes[pnt_i[i]][2] );
502         sgSubVec3( pt, center );
503
504         sgSetVec3( normal, normals[nml_i[i]][0], normals[nml_i[i]][1],
505                    normals[nml_i[i]][2] );
506
507         // calculate a vector perpendicular to dir and up
508         sgVec3 perp;
509         sgVectorProductVec3( perp, normal, nup );
510
511         // front face
512         sgVec3 tmp3;
513         sgCopyVec3( tmp3, pt );
514         vl->add( tmp3 );
515         sgAddVec3( tmp3, nup );
516         vl->add( tmp3 );
517         sgAddVec3( tmp3, perp );
518         vl->add( tmp3 );
519         // sgSubVec3( tmp3, nup );
520         // vl->add( tmp3 );
521
522         nl->add( normal );
523         nl->add( normal );
524         nl->add( normal );
525         // nl->add( normal );
526
527         sgVec4 color;
528         sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
529         cl->add( color );
530         sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 );
531         cl->add( color );
532         cl->add( color );
533         // cl->add( color );
534
535         ssgLeaf *leaf = 
536             new ssgVtxTable ( GL_TRIANGLES, vl, nl, NULL, cl );
537
538         leaf->setState( mat->get_state() );
539         leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
540         leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
541
542         rabbit->addKid( leaf );
543     }
544
545     rabbit->setDuration( 10 );
546     rabbit->setLimits( 0, pnt_i.size() - 1 );
547     rabbit->setMode( SSG_ANIM_SHUTTLE );
548     rabbit->control( SSG_ANIM_START );
549    
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 );
555
556     // create the transformation.
557     sgCoord coord;
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 );
562
563     return trans;
564 }
565
566
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 )
571 {
572
573     ssgVertexArray *vl = new ssgVertexArray( 3 );
574     ssgColourArray *cl = new ssgColourArray( 3 );
575
576     sgVec3 tmp3;
577     sgCopyVec3( tmp3, pt );
578     vl->add( tmp3 );
579     sgAddVec3( tmp3, dir );
580     vl->add( tmp3 );
581
582     sgVec4 color;
583     sgSetVec4( color, 1.0, 0.0, 0.0, 1.0 );
584     cl->add( color );
585     cl->add( color );
586
587     ssgLeaf *leaf = 
588         new ssgVtxTable ( GL_LINES, vl, NULL, NULL, cl );
589
590     SGMaterial *mat = matlib->find( "GROUND_LIGHTS" );
591     leaf->setState( mat->get_state() );
592
593     return leaf;
594 }
595 #endif
596
597
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,
604                                     sgdVec3 dup )
605 {
606     sgVec3 up;
607     sgSetVec3( up, dup );
608
609     sgVec3 nup;
610     sgNormalizeVec3( nup, up );
611
612     SGMaterial *mat = matlib->find( material );
613
614     if ( material == "RWY_REIL_LIGHTS" ) {
615         // cout << "found a reil" << endl;
616         ssgTransform *reil = gen_reil_lights( nodes, normals, pnt_i, nml_i,
617                                               matlib, up );
618         return reil;
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,
622                                                 matlib, up );
623         return odals;
624     } else if ( material == "RWY_SEQUENCED_LIGHTS" ) {
625         // cout << "found a rabbit" << endl;
626         ssgTransform *rabbit = gen_rabbit_lights( nodes, normals,
627                                                   pnt_i, nml_i,
628                                                   matlib, up );
629         return rabbit;
630     } else if ( material == "RWY_VASI_LIGHTS" ) {
631         ssgTransform *light_group = gen_dir_light_group( nodes, normals, pnt_i,
632                                                          nml_i, mat, up,
633                                                          false );
634
635         // calculate the geocentric position of this vasi and use it
636         // to init the vasi structure and save it in the userdata slot
637         sgdVec3 pos;
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 );
645
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
648         // structure.
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 );
653
654         SGVASIUserData *vasi = new SGVASIUserData( pos, leaf );
655
656         light_group->setUserData( vasi );
657
658         return light_group;
659     } else if ( material == "RWY_BLUE_TAXIWAY_LIGHTS" ) {
660         ssgTransform *light_group = gen_dir_light_group( nodes, normals, pnt_i,
661                                                          nml_i, mat, up,
662                                                          true );
663         return light_group;
664     } else {
665         ssgTransform *light_group = gen_dir_light_group( nodes, normals, pnt_i,
666                                                          nml_i, mat, up,
667                                                          false );
668         return light_group;
669     }
670
671     return NULL;
672 }