]> git.mxchange.org Git - simgear.git/blob - simgear/scene/tgdb/pt_lights.cxx
e5c9a19cd6504daa2d404c6f4b983efa9728efa0
[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 "pt_lights.hxx"
28
29 #include <osg/Array>
30 #include <osg/Geometry>
31 #include <osg/CullFace>
32 #include <osg/Geode>
33 #include <osg/MatrixTransform>
34 #include <osg/NodeCallback>
35 #include <osg/NodeVisitor>
36 #include <osg/Texture2D>
37 #include <osg/AlphaFunc>
38 #include <osg/BlendFunc>
39 #include <osg/TexEnv>
40 #include <osg/Sequence>
41 #include <osg/PolygonMode>
42 #include <osg/Fog>
43 #include <osg/FragmentProgram>
44 #include <osg/VertexProgram>
45 #include <osg/Point>
46 #include <osg/PointSprite>
47 #include <osg/Material>
48 #include <osg/Group>
49 #include <osg/StateSet>
50
51 #include <osgUtil/CullVisitor>
52
53 #include <simgear/math/sg_random.h>
54 #include <simgear/debug/logstream.hxx>
55 #include <simgear/threads/SGThread.hxx>
56 #include <simgear/threads/SGGuard.hxx>
57 #include <simgear/scene/util/RenderConstants.hxx>
58 #include <simgear/scene/util/SGEnlargeBoundingBox.hxx>
59
60 #include "SGVasiDrawable.hxx"
61
62 using namespace simgear;
63
64 static void
65 setPointSpriteImage(unsigned char* data, unsigned log2resolution,
66                     unsigned charsPerPixel)
67 {
68   int env_tex_res = (1 << log2resolution);
69   for (int i = 0; i < env_tex_res; ++i) {
70     for (int j = 0; j < env_tex_res; ++j) {
71       int xi = 2*i + 1 - env_tex_res;
72       int yi = 2*j + 1 - env_tex_res;
73       if (xi < 0)
74         xi = -xi;
75       if (yi < 0)
76         yi = -yi;
77       
78       xi -= 1;
79       yi -= 1;
80       
81       if (xi < 0)
82         xi = 0;
83       if (yi < 0)
84         yi = 0;
85       
86       float x = 1.5*xi/(float)(env_tex_res);
87       float y = 1.5*yi/(float)(env_tex_res);
88       //       float x = 2*xi/(float)(env_tex_res);
89       //       float y = 2*yi/(float)(env_tex_res);
90       float dist = sqrt(x*x + y*y);
91       float bright = SGMiscf::clip(255*(1-dist), 0, 255);
92       for (unsigned l = 0; l < charsPerPixel; ++l)
93         data[charsPerPixel*(i*env_tex_res + j) + l] = (unsigned char)bright;
94     }
95   }
96 }
97
98 static osg::Image*
99 getPointSpriteImage(int logResolution)
100 {
101   osg::Image* image = new osg::Image;
102   
103   osg::Image::MipmapDataType mipmapOffsets;
104   unsigned off = 0;
105   for (int i = logResolution; 0 <= i; --i) {
106     unsigned res = 1 << i;
107     off += res*res;
108     mipmapOffsets.push_back(off);
109   }
110   
111   int env_tex_res = (1 << logResolution);
112   
113   unsigned char* imageData = new unsigned char[off];
114   image->setImage(env_tex_res, env_tex_res, 1,
115                   GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, imageData,
116                   osg::Image::USE_NEW_DELETE);
117   image->setMipmapLevels(mipmapOffsets);
118   
119   for (int k = logResolution; 0 <= k; --k) {
120     setPointSpriteImage(image->getMipmapData(logResolution - k), k, 1);
121   }
122   
123   return image;
124 }
125
126 static osg::Texture2D*
127 gen_standard_light_sprite(void)
128 {
129   // double checked locking ...
130   static osg::ref_ptr<osg::Texture2D> texture;
131   if (texture.valid())
132     return texture.get();
133   
134   static SGMutex mutex;
135   SGGuard<SGMutex> guard(mutex);
136   if (texture.valid())
137     return texture.get();
138   
139   texture = new osg::Texture2D;
140   texture->setImage(getPointSpriteImage(6));
141   texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP);
142   texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP);
143   
144   return texture.get();
145 }
146
147 SGPointSpriteLightCullCallback::SGPointSpriteLightCullCallback(const osg::Vec3& da,
148                                                                float sz) :
149   _pointSpriteStateSet(new osg::StateSet),
150   _distanceAttenuationStateSet(new osg::StateSet)
151 {
152   osg::PointSprite* pointSprite = new osg::PointSprite;
153   _pointSpriteStateSet->setTextureAttributeAndModes(0, pointSprite,
154                                                     osg::StateAttribute::ON);
155   osg::Texture2D* texture = gen_standard_light_sprite();
156   _pointSpriteStateSet->setTextureAttribute(0, texture);
157   _pointSpriteStateSet->setTextureMode(0, GL_TEXTURE_2D,
158                                        osg::StateAttribute::ON);
159   osg::TexEnv* texEnv = new osg::TexEnv;
160   texEnv->setMode(osg::TexEnv::MODULATE);
161   _pointSpriteStateSet->setTextureAttribute(0, texEnv);
162   
163   osg::Point* point = new osg::Point;
164   point->setFadeThresholdSize(1);
165   point->setMinSize(1);
166   point->setMaxSize(sz);
167   point->setSize(sz);
168   point->setDistanceAttenuation(da);
169   _distanceAttenuationStateSet->setAttributeAndModes(point);
170 }
171
172 // FIXME make state sets static
173 SGPointSpriteLightCullCallback::SGPointSpriteLightCullCallback(osg::Point* point) :
174   _pointSpriteStateSet(new osg::StateSet),
175   _distanceAttenuationStateSet(new osg::StateSet)
176 {
177   osg::PointSprite* pointSprite = new osg::PointSprite;
178   _pointSpriteStateSet->setTextureAttributeAndModes(0, pointSprite,
179                                                     osg::StateAttribute::ON);
180   osg::Texture2D* texture = gen_standard_light_sprite();
181   _pointSpriteStateSet->setTextureAttribute(0, texture);
182   _pointSpriteStateSet->setTextureMode(0, GL_TEXTURE_2D,
183                                        osg::StateAttribute::ON);
184   osg::TexEnv* texEnv = new osg::TexEnv;
185   texEnv->setMode(osg::TexEnv::MODULATE);
186   _pointSpriteStateSet->setTextureAttribute(0, texEnv);
187   
188   _distanceAttenuationStateSet->setAttributeAndModes(point);
189 }
190
191 void
192 SGPointSpriteLightCullCallback::operator()(osg::Node* node,
193                                            osg::NodeVisitor* nv)
194 {
195   assert(dynamic_cast<osgUtil::CullVisitor*>(nv));
196   osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);
197   
198   // Test for point sprites and point parameters availibility
199   unsigned contextId = cv->getRenderInfo().getContextID();
200   SGSceneFeatures* features = SGSceneFeatures::instance();
201   bool usePointSprite = features->getEnablePointSpriteLights(contextId);
202   bool usePointParameters = features->getEnableDistanceAttenuationLights(contextId);
203   
204   if (usePointSprite)
205     cv->pushStateSet(_pointSpriteStateSet.get());
206   
207   if (usePointParameters)
208     cv->pushStateSet(_distanceAttenuationStateSet.get());
209   
210   traverse(node, nv);
211   
212   if (usePointParameters)
213     cv->popStateSet();
214   
215   if (usePointSprite)
216     cv->popStateSet();
217 }
218
219 osg::Node*
220 SGLightFactory::getLight(const SGLightBin::Light& light)
221 {
222   osg::Vec3Array* vertices = new osg::Vec3Array;
223   osg::Vec4Array* colors = new osg::Vec4Array;
224
225   vertices->push_back(light.position.osg());
226   colors->push_back(light.color.osg());
227   
228   osg::Geometry* geometry = new osg::Geometry;
229   geometry->setVertexArray(vertices);
230   geometry->setNormalBinding(osg::Geometry::BIND_OFF);
231   geometry->setColorArray(colors);
232   geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
233
234   // Enlarge the bounding box to avoid such light nodes being victim to
235   // small feature culling.
236   geometry->setComputeBoundingBoxCallback(new SGEnlargeBoundingBox(1));
237   
238   osg::DrawArrays* drawArrays;
239   drawArrays = new osg::DrawArrays(osg::PrimitiveSet::POINTS,
240                                    0, vertices->size());
241   geometry->addPrimitiveSet(drawArrays);
242   
243   osg::StateSet* stateSet = geometry->getOrCreateStateSet();
244   stateSet->setRenderBinDetails(POINT_LIGHTS_BIN, "DepthSortedBin");
245   stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
246   
247   osg::BlendFunc* blendFunc = new osg::BlendFunc;
248   stateSet->setAttribute(blendFunc);
249   stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
250   
251   osg::AlphaFunc* alphaFunc;
252   alphaFunc = new osg::AlphaFunc(osg::AlphaFunc::GREATER, 0.01);
253   stateSet->setAttribute(alphaFunc);
254   stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON);
255   
256   osg::Geode* geode = new osg::Geode;
257   geode->addDrawable(geometry);
258
259   return geode;
260 }
261
262 osg::Node*
263 SGLightFactory::getLight(const SGDirectionalLightBin::Light& light)
264 {
265   osg::Vec3Array* vertices = new osg::Vec3Array;
266   osg::Vec4Array* colors = new osg::Vec4Array;
267
268   SGVec4f visibleColor(light.color);
269   SGVec4f invisibleColor(visibleColor[0], visibleColor[1],
270                          visibleColor[2], 0);
271   SGVec3f normal = normalize(light.normal);
272   SGVec3f perp1 = perpendicular(normal);
273   SGVec3f perp2 = cross(normal, perp1);
274   SGVec3f position = light.position;
275   vertices->push_back(position.osg());
276   vertices->push_back((position + perp1).osg());
277   vertices->push_back((position + perp2).osg());
278   colors->push_back(visibleColor.osg());
279   colors->push_back(invisibleColor.osg());
280   colors->push_back(invisibleColor.osg());
281   
282   osg::Geometry* geometry = new osg::Geometry;
283   geometry->setVertexArray(vertices);
284   geometry->setNormalBinding(osg::Geometry::BIND_OFF);
285   geometry->setColorArray(colors);
286   geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
287   // Enlarge the bounding box to avoid such light nodes being victim to
288   // small feature culling.
289   geometry->setComputeBoundingBoxCallback(new SGEnlargeBoundingBox(1));
290   
291   osg::DrawArrays* drawArrays;
292   drawArrays = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES,
293                                    0, vertices->size());
294   geometry->addPrimitiveSet(drawArrays);
295   
296   osg::StateSet* stateSet = geometry->getOrCreateStateSet();
297   stateSet->setRenderBinDetails(POINT_LIGHTS_BIN, "DepthSortedBin");
298
299   osg::Material* material = new osg::Material;
300   material->setColorMode(osg::Material::OFF);
301   stateSet->setAttribute(material);
302
303   osg::CullFace* cullFace = new osg::CullFace;
304   cullFace->setMode(osg::CullFace::BACK);
305   stateSet->setAttribute(cullFace, osg::StateAttribute::ON);
306   stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::ON);
307   
308   osg::PolygonMode* polygonMode = new osg::PolygonMode;
309   polygonMode->setMode(osg::PolygonMode::FRONT, osg::PolygonMode::POINT);
310   stateSet->setAttribute(polygonMode);
311
312   stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
313   
314   osg::BlendFunc* blendFunc = new osg::BlendFunc;
315   stateSet->setAttribute(blendFunc);
316   stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
317   
318   osg::AlphaFunc* alphaFunc;
319   alphaFunc = new osg::AlphaFunc(osg::AlphaFunc::GREATER, 0.01);
320   stateSet->setAttribute(alphaFunc);
321   stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON);
322   
323   osg::Geode* geode = new osg::Geode;
324   geode->addDrawable(geometry);
325
326   return geode;
327 }
328
329 osg::Drawable*
330 SGLightFactory::getLights(const SGLightBin& lights, unsigned inc, float alphaOff)
331 {
332   if (lights.getNumLights() <= 0)
333     return 0;
334   
335   osg::Vec3Array* vertices = new osg::Vec3Array;
336   osg::Vec4Array* colors = new osg::Vec4Array;
337
338   for (unsigned i = 0; i < lights.getNumLights(); i += inc) {
339     vertices->push_back(lights.getLight(i).position.osg());
340     SGVec4f color = lights.getLight(i).color;
341     color[3] = SGMiscf::max(0, SGMiscf::min(1, color[3] + alphaOff));
342     colors->push_back(color.osg());
343   }
344   
345   osg::Geometry* geometry = new osg::Geometry;
346   
347   geometry->setVertexArray(vertices);
348   geometry->setNormalBinding(osg::Geometry::BIND_OFF);
349   geometry->setColorArray(colors);
350   geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
351   
352   osg::DrawArrays* drawArrays;
353   drawArrays = new osg::DrawArrays(osg::PrimitiveSet::POINTS,
354                                    0, vertices->size());
355   geometry->addPrimitiveSet(drawArrays);
356   
357   osg::StateSet* stateSet = geometry->getOrCreateStateSet();
358   stateSet->setRenderBinDetails(POINT_LIGHTS_BIN, "DepthSortedBin");
359
360   stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
361   
362   osg::BlendFunc* blendFunc = new osg::BlendFunc;
363   stateSet->setAttribute(blendFunc);
364   stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
365   
366   osg::AlphaFunc* alphaFunc;
367   alphaFunc = new osg::AlphaFunc(osg::AlphaFunc::GREATER, 0.01);
368   stateSet->setAttribute(alphaFunc);
369   stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON);
370   
371   return geometry;
372 }
373
374
375 osg::Drawable*
376 SGLightFactory::getLights(const SGDirectionalLightBin& lights)
377 {
378   if (lights.getNumLights() <= 0)
379     return 0;
380   
381   osg::Vec3Array* vertices = new osg::Vec3Array;
382   osg::Vec4Array* colors = new osg::Vec4Array;
383
384   for (unsigned i = 0; i < lights.getNumLights(); ++i) {
385     SGVec4f visibleColor(lights.getLight(i).color);
386     SGVec4f invisibleColor(visibleColor[0], visibleColor[1],
387                            visibleColor[2], 0);
388     SGVec3f normal = normalize(lights.getLight(i).normal);
389     SGVec3f perp1 = perpendicular(normal);
390     SGVec3f perp2 = cross(normal, perp1);
391     SGVec3f position = lights.getLight(i).position;
392     vertices->push_back(position.osg());
393     vertices->push_back((position + perp1).osg());
394     vertices->push_back((position + perp2).osg());
395     colors->push_back(visibleColor.osg());
396     colors->push_back(invisibleColor.osg());
397     colors->push_back(invisibleColor.osg());
398   }
399   
400   osg::Geometry* geometry = new osg::Geometry;
401   
402   geometry->setVertexArray(vertices);
403   geometry->setNormalBinding(osg::Geometry::BIND_OFF);
404   geometry->setColorArray(colors);
405   geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
406   
407   osg::DrawArrays* drawArrays;
408   drawArrays = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES,
409                                    0, vertices->size());
410   geometry->addPrimitiveSet(drawArrays);
411   
412   osg::StateSet* stateSet = geometry->getOrCreateStateSet();
413   stateSet->setRenderBinDetails(POINT_LIGHTS_BIN, "DepthSortedBin");
414
415   osg::Material* material = new osg::Material;
416   material->setColorMode(osg::Material::OFF);
417   stateSet->setAttribute(material);
418
419   osg::CullFace* cullFace = new osg::CullFace;
420   cullFace->setMode(osg::CullFace::BACK);
421   stateSet->setAttribute(cullFace, osg::StateAttribute::ON);
422   stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::ON);
423   
424   osg::PolygonMode* polygonMode = new osg::PolygonMode;
425   polygonMode->setMode(osg::PolygonMode::FRONT, osg::PolygonMode::POINT);
426   stateSet->setAttribute(polygonMode);
427
428   stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
429   
430   osg::BlendFunc* blendFunc = new osg::BlendFunc;
431   stateSet->setAttribute(blendFunc);
432   stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
433   
434   osg::AlphaFunc* alphaFunc;
435   alphaFunc = new osg::AlphaFunc(osg::AlphaFunc::GREATER, 0.01);
436   stateSet->setAttribute(alphaFunc);
437   stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON);
438   
439   return geometry;
440 }
441
442 static SGVasiDrawable*
443 buildVasi(const SGDirectionalLightBin& lights, const SGVec3f& up,
444        const SGVec4f& red, const SGVec4f& white)
445 {
446   unsigned count = lights.getNumLights();
447   if ( count == 4 ) {
448     SGVasiDrawable* drawable = new SGVasiDrawable(red, white);
449
450     // PAPI configuration
451     // papi D
452     drawable->addLight(lights.getLight(0).position,
453                        lights.getLight(0).normal, up, 3.5);
454     // papi C
455     drawable->addLight(lights.getLight(1).position,
456                        lights.getLight(1).normal, up, 3.167);
457     // papi B
458     drawable->addLight(lights.getLight(2).position,
459                        lights.getLight(2).normal, up, 2.833);
460     // papi A
461     drawable->addLight(lights.getLight(3).position,
462                        lights.getLight(3).normal, up, 2.5);
463     return drawable;
464   }
465   else if (count == 12) {
466     SGVasiDrawable* drawable = new SGVasiDrawable(red, white);
467     
468     // probably vasi, first 6 are downwind bar (2.5 deg)
469     for (unsigned i = 0; i < 6; ++i)
470       drawable->addLight(lights.getLight(i).position,
471                          lights.getLight(i).normal, up, 2.5);
472     // last 6 are upwind bar (3.0 deg)
473     for (unsigned i = 6; i < 12; ++i)
474       drawable->addLight(lights.getLight(i).position,
475                          lights.getLight(i).normal, up, 3.0);
476     
477     return drawable;
478   } else {
479     // fail safe
480     SG_LOG(SG_TERRAIN, SG_ALERT,
481            "unknown vasi/papi configuration, count = " << count);
482     return 0;
483   }
484 }
485
486 osg::Drawable*
487 SGLightFactory::getVasi(const SGVec3f& up, const SGDirectionalLightBin& lights,
488                         const SGVec4f& red, const SGVec4f& white)
489 {
490   SGVasiDrawable* drawable = buildVasi(lights, up, red, white);
491   if (!drawable)
492     return 0;
493
494   osg::StateSet* stateSet = drawable->getOrCreateStateSet();
495   stateSet->setRenderBinDetails(POINT_LIGHTS_BIN, "DepthSortedBin");
496   stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
497
498   osg::BlendFunc* blendFunc = new osg::BlendFunc;
499   stateSet->setAttribute(blendFunc);
500   stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
501   
502   osg::AlphaFunc* alphaFunc;
503   alphaFunc = new osg::AlphaFunc(osg::AlphaFunc::GREATER, 0.01);
504   stateSet->setAttribute(alphaFunc);
505   stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON);
506
507   return drawable;
508 }
509
510 osg::Node*
511 SGLightFactory::getSequenced(const SGDirectionalLightBin& lights)
512 {
513   if (lights.getNumLights() <= 0)
514     return 0;
515
516   // generate a repeatable random seed
517   sg_srandom(unsigned(lights.getLight(0).position[0]));
518   float flashTime = 2e-2 + 5e-3*sg_random();
519   osg::Sequence* sequence = new osg::Sequence;
520   sequence->setDefaultTime(flashTime);
521
522   for (int i = lights.getNumLights() - 1; 0 <= i; --i)
523     sequence->addChild(getLight(lights.getLight(i)), flashTime);
524   sequence->addChild(new osg::Group, 1 + 1e-1*sg_random());
525   sequence->setInterval(osg::Sequence::LOOP, 0, -1);
526   sequence->setDuration(1.0f, -1);
527   sequence->setMode(osg::Sequence::START);
528   sequence->setSync(true);
529
530   osg::StateSet* stateSet = sequence->getOrCreateStateSet();
531   stateSet->setRenderBinDetails(POINT_LIGHTS_BIN, "DepthSortedBin");
532   stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
533
534   osg::BlendFunc* blendFunc = new osg::BlendFunc;
535   stateSet->setAttribute(blendFunc);
536   stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
537   
538   osg::AlphaFunc* alphaFunc;
539   alphaFunc = new osg::AlphaFunc(osg::AlphaFunc::GREATER, 0.01);
540   stateSet->setAttribute(alphaFunc);
541   stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON);
542
543   osg::Point* point = new osg::Point;
544   point->setMinSize(6);
545   point->setMaxSize(10);
546   point->setSize(10);
547   point->setDistanceAttenuation(osg::Vec3(1.0, 0.0001, 0.00000001));
548   sequence->setCullCallback(new SGPointSpriteLightCullCallback(point));
549
550   return sequence;
551 }
552
553 osg::Node*
554 SGLightFactory::getOdal(const SGLightBin& lights)
555 {
556   if (lights.getNumLights() < 2)
557     return 0;
558
559   // generate a repeatable random seed
560   sg_srandom(unsigned(lights.getLight(0).position[0]));
561   float flashTime = 2e-2 + 5e-3*sg_random();
562   osg::Sequence* sequence = new osg::Sequence;
563   sequence->setDefaultTime(flashTime);
564
565   // centerline lights
566   for (int i = lights.getNumLights() - 1; 2 <= i; --i)
567     sequence->addChild(getLight(lights.getLight(i)), flashTime);
568
569   // runway end lights
570   osg::Group* group = new osg::Group;
571   for (unsigned i = 0; i < 2; ++i)
572     group->addChild(getLight(lights.getLight(i)));
573   sequence->addChild(group, flashTime);
574
575   // add an extra empty group for a break
576   sequence->addChild(new osg::Group, 9 + 1e-1*sg_random());
577   sequence->setInterval(osg::Sequence::LOOP, 0, -1);
578   sequence->setDuration(1.0f, -1);
579   sequence->setMode(osg::Sequence::START);
580   sequence->setSync(true);
581
582   osg::StateSet* stateSet = sequence->getOrCreateStateSet();
583   stateSet->setRenderBinDetails(POINT_LIGHTS_BIN, "DepthSortedBin");
584   stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
585
586   osg::BlendFunc* blendFunc = new osg::BlendFunc;
587   stateSet->setAttribute(blendFunc);
588   stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
589   
590   osg::AlphaFunc* alphaFunc;
591   alphaFunc = new osg::AlphaFunc(osg::AlphaFunc::GREATER, 0.01);
592   stateSet->setAttribute(alphaFunc);
593   stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON);
594
595   osg::Point* point = new osg::Point;
596   point->setMinSize(6);
597   point->setMaxSize(10);
598   point->setSize(10);
599   point->setDistanceAttenuation(osg::Vec3(1.0, 0.0001, 0.00000001));
600   sequence->setCullCallback(new SGPointSpriteLightCullCallback(point));
601
602   return sequence;
603 }