]> git.mxchange.org Git - simgear.git/blob - simgear/scene/tgdb/pt_lights.cxx
Modified Files:
[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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22
23 #ifdef HAVE_CONFIG_H
24 #  include <simgear_config.h>
25 #endif
26
27 #include <osg/Array>
28 #include <osg/Geometry>
29 #include <osg/Geode>
30 #include <osg/LOD>
31 #include <osg/MatrixTransform>
32 #include <osg/NodeCallback>
33 #include <osg/NodeVisitor>
34 #include <osg/Switch>
35
36 #include <simgear/scene/material/mat.hxx>
37 #include <simgear/screen/extensions.hxx>
38 #include <simgear/math/sg_random.h>
39
40 #include "vasi.hxx"
41
42 #include "pt_lights.hxx"
43
44 // static variables for use in ssg callbacks
45 bool SGPointLightsUseSprites = false;
46 bool SGPointLightsEnhancedLighting = false;
47 bool SGPointLightsDistanceAttenuation = false;
48
49
50 // Specify the way we want to draw directional point lights (assuming the
51 // appropriate extensions are available.)
52
53 void SGConfigureDirectionalLights( bool use_point_sprites,
54                                    bool enhanced_lighting,
55                                    bool distance_attenuation ) {
56     SGPointLightsUseSprites = use_point_sprites;
57     SGPointLightsEnhancedLighting = enhanced_lighting;
58     SGPointLightsDistanceAttenuation = distance_attenuation;
59 }
60
61 static void calc_center_point( const point_list &nodes,
62                                const int_list &pnt_i,
63                                Point3D& result ) {
64     double minx = nodes[pnt_i[0]][0];
65     double maxx = nodes[pnt_i[0]][0];
66     double miny = nodes[pnt_i[0]][1];
67     double maxy = nodes[pnt_i[0]][1];
68     double minz = nodes[pnt_i[0]][2];
69     double maxz = nodes[pnt_i[0]][2];
70
71     for ( unsigned int i = 0; i < pnt_i.size(); ++i ) {
72         Point3D pt = nodes[pnt_i[i]];
73         if ( pt[0] < minx ) { minx = pt[0]; }
74         if ( pt[0] > maxx ) { minx = pt[0]; }
75         if ( pt[1] < miny ) { miny = pt[1]; }
76         if ( pt[1] > maxy ) { miny = pt[1]; }
77         if ( pt[2] < minz ) { minz = pt[2]; }
78         if ( pt[2] > maxz ) { minz = pt[2]; }
79     }
80
81     result = Point3D((minx + maxx) / 2.0, (miny + maxy) / 2.0,
82                      (minz + maxz) / 2.0);
83 }
84
85
86 static osg::Node*
87 gen_dir_light_group( const point_list &nodes,
88                      const point_list &normals,
89                      const int_list &pnt_i,
90                      const int_list &nml_i,
91                      const SGMaterial *mat,
92                      const osg::Vec3& up, bool vertical, bool vasi )
93 {
94     Point3D center;
95     calc_center_point( nodes, pnt_i, center );
96     // cout << center[0] << "," << center[1] << "," << center[2] << endl;
97
98
99     // find a vector perpendicular to the normal.
100     osg::Vec3 perp1;
101     if ( !vertical ) {
102         // normal isn't vertical so we can use up as our first vector
103         perp1 = up;
104     } else {
105         // normal is vertical so we have to work a bit harder to
106         // determine our first vector
107         osg::Vec3 pt1(nodes[pnt_i[0]][0], nodes[pnt_i[0]][1],
108                       nodes[pnt_i[0]][2] );
109         osg::Vec3 pt2(nodes[pnt_i[1]][0], nodes[pnt_i[1]][1],
110                       nodes[pnt_i[1]][2] );
111
112         perp1 = pt2 - pt1;
113     }
114     perp1.normalize();
115
116     osg::Vec3Array *vl = new osg::Vec3Array;
117     osg::Vec3Array *nl = new osg::Vec3Array;
118     osg::Vec4Array *cl = new osg::Vec4Array;
119
120     unsigned int i;
121     for ( i = 0; i < pnt_i.size(); ++i ) {
122         Point3D ppt = nodes[pnt_i[i]] - center;
123         osg::Vec3 pt(ppt[0], ppt[1], ppt[2]);
124         osg::Vec3 normal(normals[nml_i[i]][0], normals[nml_i[i]][1],
125                          normals[nml_i[i]][2] );
126
127         // calculate a vector perpendicular to dir and up
128         osg::Vec3 perp2 = normal^perp1;
129
130         // front face
131         osg::Vec3 tmp3 = pt;
132         vl->push_back( tmp3 );
133         tmp3 += perp1;
134         vl->push_back( tmp3 );
135         tmp3 += perp2;
136         vl->push_back( tmp3 );
137
138         nl->push_back( normal );
139         nl->push_back( normal );
140         nl->push_back( normal );
141
142         cl->push_back(osg::Vec4(1, 1, 1, 1));
143         cl->push_back(osg::Vec4(1, 1, 1, 0));
144         cl->push_back(osg::Vec4(1, 1, 1, 0));
145     }
146
147     osg::Geometry* geometry = new osg::Geometry;
148     geometry->setName("Dir Lights " + mat->get_names().front());
149     geometry->setVertexArray(vl);
150     geometry->setNormalArray(nl);
151     geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
152     geometry->setColorArray(cl);
153     geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
154     geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLES, 0, vl->size()));
155     osg::Geode* geode = new osg::Geode;
156     geode->addDrawable(geometry);
157
158     if (vasi) {
159         // this one is dynamic in its colors, so do not bother with dlists
160         geometry->setUseDisplayList(false);
161         geometry->setUseVertexBufferObjects(false);
162         osg::Vec3 dir(normals[nml_i[0]][0], normals[nml_i[0]][1],
163                       normals[nml_i[0]][2]);
164
165         // calculate the reference position of this vasi and use it
166         // to init the vasi structure
167         Point3D ppt = nodes[pnt_i[0]] - center;
168         osg::Vec3 pt(ppt[0], ppt[1], ppt[2]);
169         // up is the "up" vector which is also
170         // the reference center point of this tile.  The reference
171         // center + the coordinate of the first light gives the actual
172         // location of the first light.
173         pt += up;
174
175         // Set up the callback
176         geode->setCullCallback(new SGVasiUpdateCallback(cl, pt, up, dir));
177     }
178
179     if ( mat != NULL ) {
180         geode->setStateSet(mat->get_state());
181     } else {
182         SG_LOG( SG_TERRAIN, SG_ALERT, "Warning: material = NULL" );
183     }
184
185     // put an LOD on each lighting component
186     osg::LOD *lod = new osg::LOD;
187     lod->addChild( geode, 0, 20000 );
188
189     // create the transformation.
190     osg::MatrixTransform *trans = new osg::MatrixTransform;
191     trans->setMatrix(osg::Matrixd::translate(osg::Vec3d(center[0], center[1], center[2])));
192     trans->addChild( lod );
193
194     return trans;
195 }
196
197 static osg::Node *gen_reil_lights( const point_list &nodes,
198                                    const point_list &normals,
199                                    const int_list &pnt_i,
200                                    const int_list &nml_i,
201                                    SGMaterialLib *matlib,
202                                    const osg::Vec3& up )
203 {
204     Point3D center;
205     calc_center_point( nodes, pnt_i, center );
206     // cout << center[0] << "," << center[1] << "," << center[2] << endl;
207
208     osg::Vec3 nup = up;
209     nup.normalize();
210
211     osg::Vec3Array   *vl = new osg::Vec3Array;
212     osg::Vec3Array   *nl = new osg::Vec3Array;
213     osg::Vec4Array   *cl = new osg::Vec4Array;
214
215     unsigned int i;
216     for ( i = 0; i < pnt_i.size(); ++i ) {
217         Point3D ppt = nodes[pnt_i[i]] - center;
218         osg::Vec3 pt(ppt[0], ppt[1], ppt[2]);
219         osg::Vec3 normal(normals[nml_i[i]][0], normals[nml_i[i]][1],
220                          normals[nml_i[i]][2] );
221
222         // calculate a vector perpendicular to dir and up
223         osg::Vec3 perp = normal^up;
224
225         // front face
226         osg::Vec3 tmp3 = pt;
227         vl->push_back( tmp3 );
228         tmp3 += nup;
229         vl->push_back( tmp3 );
230         tmp3 += perp;
231         vl->push_back( tmp3 );
232
233         nl->push_back( normal );
234         nl->push_back( normal );
235         nl->push_back( normal );
236
237         cl->push_back(osg::Vec4(1, 1, 1, 1));
238         cl->push_back(osg::Vec4(1, 1, 1, 0));
239         cl->push_back(osg::Vec4(1, 1, 1, 0));
240     }
241
242     SGMaterial *mat = matlib->find( "RWY_WHITE_LIGHTS" );
243
244     osg::Geometry* geometry = new osg::Geometry;
245     geometry->setName("Reil Lights " + mat->get_names().front());
246     geometry->setVertexArray(vl);
247     geometry->setNormalArray(nl);
248     geometry->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
249     geometry->setColorArray(cl);
250     geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
251     geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLES, 0, vl->size()));
252     osg::Geode* geode = new osg::Geode;
253     geode->addDrawable(geometry);
254
255     if ( mat != NULL ) {
256         geode->setStateSet( mat->get_state() );
257     } else {
258         SG_LOG( SG_TERRAIN, SG_ALERT,
259                 "Warning: can't find material = RWY_WHITE_LIGHTS" );
260     }
261
262     // OSGFIXME
263 //     leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
264 //     leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
265
266     // OSGFIXME: implement an update callback that switches on/off
267     // based on the osg::FrameStamp
268     osg::Switch *reil = new osg::Switch;
269 //     reil->setDuration( 60 );
270 //     reil->setLimits( 0, 2 );
271 //     reil->setMode( SSG_ANIM_SHUTTLE );
272 //     reil->control( SSG_ANIM_START );
273
274     // need to add this twice to work around an ssg bug
275     reil->addChild(geode, true);
276    
277     // put an LOD on each lighting component
278     osg::LOD *lod = new osg::LOD;
279     lod->addChild( reil, 0, 12000 /*OSGFIXME: hardcoded here?*/);
280
281     // create the transformation.
282     osg::MatrixTransform *trans = new osg::MatrixTransform;
283     trans->setMatrix(osg::Matrixd::translate(osg::Vec3d(center[0], center[1], center[2])));
284     trans->addChild( lod );
285
286     return trans;
287 }
288
289
290 static osg::Node *gen_odals_lights( const point_list &nodes,
291                                                const point_list &normals,
292                                                const int_list &pnt_i,
293                                                const int_list &nml_i,
294                                                SGMaterialLib *matlib,
295                                                const osg::Vec3& up )
296 {
297     Point3D center;
298     calc_center_point( nodes, pnt_i, center );
299     // cout << center[0] << "," << center[1] << "," << center[2] << endl;
300
301     // OSGFIXME: implement like above
302 //     osg::Switch *odals = new osg::Switch;
303     osg::Group *odals = new osg::Group;
304
305     // we don't want directional lights here
306     SGMaterial *mat = matlib->find( "GROUND_LIGHTS" );
307     if ( mat == NULL ) {
308         SG_LOG( SG_TERRAIN, SG_ALERT,
309                 "Warning: can't material = GROUND_LIGHTS" );
310     }
311
312     osg::Vec3Array   *vl = new osg::Vec3Array;
313     osg::Vec4Array   *cl = new osg::Vec4Array;
314      
315     cl->push_back(osg::Vec4(1, 1, 1, 1));
316
317     // center line strobes
318     for ( unsigned i = pnt_i.size() - 1; i >= 2; --i ) {
319         Point3D ppt = nodes[pnt_i[i]] - center;
320         osg::Vec3 pt(ppt[0], ppt[1], ppt[2]);
321         vl->push_back(pt);
322     }
323
324     // runway end strobes
325      
326     Point3D ppt = nodes[pnt_i[0]] - center;
327     osg::Vec3 pt(ppt[0], ppt[1], ppt[2]);
328     vl->push_back(pt);
329
330     ppt = nodes[pnt_i[1]] - center;
331     pt = osg::Vec3(ppt[0], ppt[1], ppt[2]);
332     vl->push_back(pt);
333
334     osg::Geometry* geometry = new osg::Geometry;
335     geometry->setName("Odal Lights " + mat->get_names().front());
336     geometry->setVertexArray(vl);
337     geometry->setColorArray(cl);
338     geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
339     geometry->addPrimitiveSet(new osg::DrawArrays(GL_POINTS, 0, vl->size()));
340     osg::Geode* geode = new osg::Geode;
341     geode->addDrawable(geometry);
342
343     geode->setStateSet( mat->get_state() );
344     // OSGFIXME
345 //         leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
346 //         leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
347
348     odals->addChild( geode );
349
350     // setup animition
351
352 //     odals->setDuration( 10 );
353 //     odals->setLimits( 0, pnt_i.size() - 1 );
354 //     odals->setMode( SSG_ANIM_SHUTTLE );
355 //     odals->control( SSG_ANIM_START );
356    
357     // put an LOD on each lighting component
358     osg::LOD *lod = new osg::LOD;
359     lod->addChild( odals, 0, 12000 /*OSGFIXME hardcoded visibility*/ );
360
361     // create the transformation.
362     osg::MatrixTransform *trans = new osg::MatrixTransform;
363     trans->setMatrix(osg::Matrixd::translate(osg::Vec3d(center[0], center[1], center[2])));
364     trans->addChild(lod);
365
366     return trans;
367 }
368
369 class SGRabbitUpdateCallback : public osg::NodeCallback {
370 public:
371   SGRabbitUpdateCallback(double duration) :
372     mBaseTime(sg_random()), mDuration(duration)
373   {
374     if (fabs(mDuration) < 1e-3)
375       mDuration = 1e-3;
376     mBaseTime -= mDuration*floor(mBaseTime/mDuration);
377   }
378
379   virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
380   {
381     assert(dynamic_cast<osg::Switch*>(node));
382     osg::Switch* sw = static_cast<osg::Switch*>(node);
383     double frameTime = nv->getFrameStamp()->getReferenceTime();
384     double timeDiff = (frameTime - mBaseTime)/mDuration;
385     double reminder = timeDiff - unsigned(floor(timeDiff));
386     unsigned nChildren = sw->getNumChildren();
387     unsigned activeChild = unsigned(nChildren*reminder);
388     if (nChildren <= activeChild)
389       activeChild = nChildren;
390     sw->setSingleChildOn(activeChild);
391
392     osg::NodeCallback::operator()(node, nv);
393   }
394 public:
395   double mBaseTime;
396   double mDuration;
397 };
398
399
400 static osg::Node *gen_rabbit_lights( const point_list &nodes,
401                                      const point_list &normals,
402                                      const int_list &pnt_i,
403                                      const int_list &nml_i,
404                                      SGMaterialLib *matlib,
405                                      const osg::Vec3& up )
406 {
407     Point3D center;
408     calc_center_point( nodes, pnt_i, center );
409     // cout << center[0] << "," << center[1] << "," << center[2] << endl;
410
411     osg::Vec3 nup = up;
412     nup.normalize();
413
414     // OSGFIXME: implement like above ...
415     osg::Switch *rabbit = new osg::Switch;
416     rabbit->setUpdateCallback(new SGRabbitUpdateCallback(10));
417
418     SGMaterial *mat = matlib->find( "RWY_WHITE_LIGHTS" );
419     if ( mat == NULL ) {
420         SG_LOG( SG_TERRAIN, SG_ALERT,
421                 "Warning: can't material = RWY_WHITE_LIGHTS" );
422     }
423
424     for ( int i = pnt_i.size() - 1; i >= 0; --i ) {
425         osg::Vec3Array   *vl = new osg::Vec3Array;
426         osg::Vec3Array   *nl = new osg::Vec3Array;
427         osg::Vec4Array   *cl = new osg::Vec4Array;
428
429
430         Point3D ppt = nodes[pnt_i[i]] - center;
431         osg::Vec3 pt(ppt[0], ppt[1], ppt[2]);
432         osg::Vec3 normal(normals[nml_i[i]][0], normals[nml_i[i]][1],
433                          normals[nml_i[i]][2] );
434
435         // calculate a vector perpendicular to dir and up
436         osg::Vec3 perp = normal^nup;
437
438         // front face
439         osg::Vec3 tmp3 = pt;
440         vl->push_back( tmp3 );
441         tmp3 += nup;
442         vl->push_back( tmp3 );
443         tmp3 += perp;
444         vl->push_back( tmp3 );
445
446         nl->push_back(normal);
447
448         cl->push_back(osg::Vec4(1, 1, 1, 1));
449         cl->push_back(osg::Vec4(1, 1, 1, 0));
450         cl->push_back(osg::Vec4(1, 1, 1, 0));
451
452         osg::Geometry* geometry = new osg::Geometry;
453         geometry->setName("Rabbit Lights " + mat->get_names().front());
454         geometry->setVertexArray(vl);
455         geometry->setNormalArray(nl);
456         geometry->setNormalBinding(osg::Geometry::BIND_OVERALL);
457         geometry->setColorArray(cl);
458         geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
459         geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLES, 0, vl->size()));
460         osg::Geode* geode = new osg::Geode;
461         geode->addDrawable(geometry);
462         
463         geode->setStateSet( mat->get_state() );
464
465         // OSGFIXME
466 //         leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
467 //         leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
468
469         rabbit->addChild( geode );
470     }
471
472 //     rabbit->setDuration( 10 );
473 //     rabbit->setLimits( 0, pnt_i.size() - 1 );
474 //     rabbit->setMode( SSG_ANIM_SHUTTLE );
475 //     rabbit->control( SSG_ANIM_START );
476    
477     // put an LOD on each lighting component
478     osg::LOD *lod = new osg::LOD;
479     lod->addChild( rabbit, 0, 12000 /*OSGFIXME: hadcoded*/ );
480
481     // create the transformation.
482     osg::MatrixTransform *trans = new osg::MatrixTransform;
483     trans->setMatrix(osg::Matrixd::translate(osg::Vec3d(center[0], center[1], center[2])));
484     trans->addChild(lod);
485
486     return trans;
487 }
488
489
490 osg::Node *SGMakeDirectionalLights( const point_list &nodes,
491                                     const point_list &normals,
492                                     const int_list &pnt_i,
493                                     const int_list &nml_i,
494                                     SGMaterialLib *matlib,
495                                     const string &material,
496                                     const SGVec3d& dup )
497 {
498     osg::Vec3 nup = toVec3f(dup).osg();
499     nup.normalize();
500
501     SGMaterial *mat = matlib->find( material );
502
503     if ( material == "RWY_REIL_LIGHTS" ) {
504         // cout << "found a reil" << endl;
505         return gen_reil_lights( nodes, normals, pnt_i, nml_i,
506                                 matlib, nup );
507     } else if ( material == "RWY_ODALS_LIGHTS" ) {
508         // cout << "found a odals" << endl;
509         return gen_odals_lights( nodes, normals, pnt_i, nml_i,
510                                  matlib, nup );
511     } else if ( material == "RWY_SEQUENCED_LIGHTS" ) {
512         // cout << "found a rabbit" << endl;
513         return gen_rabbit_lights( nodes, normals, pnt_i, nml_i,
514                                   matlib, nup );
515     } else if ( material == "RWY_VASI_LIGHTS" ) {
516         return gen_dir_light_group( nodes, normals, pnt_i,
517                                     nml_i, mat, nup, false, true );
518     } else if ( material == "RWY_BLUE_TAXIWAY_LIGHTS" ) {
519         return gen_dir_light_group( nodes, normals, pnt_i, nml_i, mat, nup,
520                                     true, false );
521     } else {
522         return gen_dir_light_group( nodes, normals, pnt_i, nml_i, mat, nup,
523                                     false, false );
524     }
525
526     return NULL;
527 }