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>
28 #include <osg/Geometry>
31 #include <osg/MatrixTransform>
32 #include <osg/NodeCallback>
33 #include <osg/NodeVisitor>
36 #include <simgear/scene/material/mat.hxx>
37 #include <simgear/screen/extensions.hxx>
38 #include <simgear/math/sg_random.h>
42 #include "pt_lights.hxx"
44 // static variables for use in ssg callbacks
45 bool SGPointLightsUseSprites = false;
46 bool SGPointLightsEnhancedLighting = false;
47 bool SGPointLightsDistanceAttenuation = false;
50 // Specify the way we want to draw directional point lights (assuming the
51 // appropriate extensions are available.)
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;
61 static void calc_center_point( const point_list &nodes,
62 const int_list &pnt_i,
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];
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]; }
81 result = Point3D((minx + maxx) / 2.0, (miny + maxy) / 2.0,
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 )
95 calc_center_point( nodes, pnt_i, center );
96 // cout << center[0] << "," << center[1] << "," << center[2] << endl;
99 // find a vector perpendicular to the normal.
102 // normal isn't vertical so we can use up as our first vector
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] );
116 osg::Vec3Array *vl = new osg::Vec3Array;
117 osg::Vec3Array *nl = new osg::Vec3Array;
118 osg::Vec4Array *cl = new osg::Vec4Array;
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] );
127 // calculate a vector perpendicular to dir and up
128 osg::Vec3 perp2 = normal^perp1;
132 vl->push_back( tmp3 );
134 vl->push_back( tmp3 );
136 vl->push_back( tmp3 );
138 nl->push_back( normal );
139 nl->push_back( normal );
140 nl->push_back( normal );
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));
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);
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]);
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.
175 // Set up the callback
176 geode->setCullCallback(new SGVasiUpdateCallback(cl, pt, up, dir));
180 geode->setStateSet(mat->get_state());
182 SG_LOG( SG_TERRAIN, SG_ALERT, "Warning: material = NULL" );
185 // put an LOD on each lighting component
186 osg::LOD *lod = new osg::LOD;
187 lod->addChild( geode, 0, 20000 );
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 );
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 )
205 calc_center_point( nodes, pnt_i, center );
206 // cout << center[0] << "," << center[1] << "," << center[2] << endl;
211 osg::Vec3Array *vl = new osg::Vec3Array;
212 osg::Vec3Array *nl = new osg::Vec3Array;
213 osg::Vec4Array *cl = new osg::Vec4Array;
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] );
222 // calculate a vector perpendicular to dir and up
223 osg::Vec3 perp = normal^up;
227 vl->push_back( tmp3 );
229 vl->push_back( tmp3 );
231 vl->push_back( tmp3 );
233 nl->push_back( normal );
234 nl->push_back( normal );
235 nl->push_back( normal );
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));
242 SGMaterial *mat = matlib->find( "RWY_WHITE_LIGHTS" );
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);
256 geode->setStateSet( mat->get_state() );
258 SG_LOG( SG_TERRAIN, SG_ALERT,
259 "Warning: can't find material = RWY_WHITE_LIGHTS" );
263 // leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
264 // leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
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 );
274 // need to add this twice to work around an ssg bug
275 reil->addChild(geode, true);
277 // put an LOD on each lighting component
278 osg::LOD *lod = new osg::LOD;
279 lod->addChild( reil, 0, 12000 /*OSGFIXME: hardcoded here?*/);
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 );
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 )
298 calc_center_point( nodes, pnt_i, center );
299 // cout << center[0] << "," << center[1] << "," << center[2] << endl;
301 // OSGFIXME: implement like above
302 // osg::Switch *odals = new osg::Switch;
303 osg::Group *odals = new osg::Group;
305 // we don't want directional lights here
306 SGMaterial *mat = matlib->find( "GROUND_LIGHTS" );
308 SG_LOG( SG_TERRAIN, SG_ALERT,
309 "Warning: can't material = GROUND_LIGHTS" );
312 osg::Vec3Array *vl = new osg::Vec3Array;
313 osg::Vec4Array *cl = new osg::Vec4Array;
315 cl->push_back(osg::Vec4(1, 1, 1, 1));
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]);
324 // runway end strobes
326 Point3D ppt = nodes[pnt_i[0]] - center;
327 osg::Vec3 pt(ppt[0], ppt[1], ppt[2]);
330 ppt = nodes[pnt_i[1]] - center;
331 pt = osg::Vec3(ppt[0], ppt[1], ppt[2]);
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);
343 geode->setStateSet( mat->get_state() );
345 // leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
346 // leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
348 odals->addChild( geode );
352 // odals->setDuration( 10 );
353 // odals->setLimits( 0, pnt_i.size() - 1 );
354 // odals->setMode( SSG_ANIM_SHUTTLE );
355 // odals->control( SSG_ANIM_START );
357 // put an LOD on each lighting component
358 osg::LOD *lod = new osg::LOD;
359 lod->addChild( odals, 0, 12000 /*OSGFIXME hardcoded visibility*/ );
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);
369 class SGRabbitUpdateCallback : public osg::NodeCallback {
371 SGRabbitUpdateCallback(double duration) :
372 mBaseTime(sg_random()), mDuration(duration)
374 if (fabs(mDuration) < 1e-3)
376 mBaseTime -= mDuration*floor(mBaseTime/mDuration);
379 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
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);
392 osg::NodeCallback::operator()(node, nv);
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 )
408 calc_center_point( nodes, pnt_i, center );
409 // cout << center[0] << "," << center[1] << "," << center[2] << endl;
414 // OSGFIXME: implement like above ...
415 osg::Switch *rabbit = new osg::Switch;
416 rabbit->setUpdateCallback(new SGRabbitUpdateCallback(10));
418 SGMaterial *mat = matlib->find( "RWY_WHITE_LIGHTS" );
420 SG_LOG( SG_TERRAIN, SG_ALERT,
421 "Warning: can't material = RWY_WHITE_LIGHTS" );
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;
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] );
435 // calculate a vector perpendicular to dir and up
436 osg::Vec3 perp = normal^nup;
440 vl->push_back( tmp3 );
442 vl->push_back( tmp3 );
444 vl->push_back( tmp3 );
446 nl->push_back(normal);
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));
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);
463 geode->setStateSet( mat->get_state() );
466 // leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw );
467 // leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
469 rabbit->addChild( geode );
472 // rabbit->setDuration( 10 );
473 // rabbit->setLimits( 0, pnt_i.size() - 1 );
474 // rabbit->setMode( SSG_ANIM_SHUTTLE );
475 // rabbit->control( SSG_ANIM_START );
477 // put an LOD on each lighting component
478 osg::LOD *lod = new osg::LOD;
479 lod->addChild( rabbit, 0, 12000 /*OSGFIXME: hadcoded*/ );
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);
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,
498 osg::Vec3 nup = toVec3f(dup).osg();
501 SGMaterial *mat = matlib->find( material );
503 if ( material == "RWY_REIL_LIGHTS" ) {
504 // cout << "found a reil" << endl;
505 return gen_reil_lights( nodes, normals, pnt_i, nml_i,
507 } else if ( material == "RWY_ODALS_LIGHTS" ) {
508 // cout << "found a odals" << endl;
509 return gen_odals_lights( nodes, normals, pnt_i, nml_i,
511 } else if ( material == "RWY_SEQUENCED_LIGHTS" ) {
512 // cout << "found a rabbit" << endl;
513 return gen_rabbit_lights( nodes, normals, pnt_i, nml_i,
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,
522 return gen_dir_light_group( nodes, normals, pnt_i, nml_i, mat, nup,