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