]> git.mxchange.org Git - simgear.git/blob - simgear/scene/tgdb/pt_lights.cxx
Fix #1783: repeated error message on console
[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 <map>
30 #include <boost/tuple/tuple_comparison.hpp>
31
32 #include <osg/Array>
33 #include <osg/Geometry>
34 #include <osg/CullFace>
35 #include <osg/Geode>
36 #include <osg/MatrixTransform>
37 #include <osg/NodeCallback>
38 #include <osg/NodeVisitor>
39 #include <osg/Texture2D>
40 #include <osg/AlphaFunc>
41 #include <osg/BlendFunc>
42 #include <osg/TexEnv>
43 #include <osg/Sequence>
44 #include <osg/Fog>
45 #include <osg/FragmentProgram>
46 #include <osg/VertexProgram>
47 #include <osg/Point>
48 #include <osg/Material>
49 #include <osg/Group>
50 #include <osg/StateSet>
51
52 #include <osgUtil/CullVisitor>
53
54 #include <OpenThreads/Mutex>
55 #include <OpenThreads/ScopedLock>
56
57 #include <simgear/math/sg_random.h>
58 #include <simgear/debug/logstream.hxx>
59 #include <simgear/scene/util/RenderConstants.hxx>
60 #include <simgear/scene/util/SGEnlargeBoundingBox.hxx>
61 #include <simgear/scene/util/OsgMath.hxx>
62 #include <simgear/scene/util/StateAttributeFactory.hxx>
63
64 #include <simgear/scene/material/Effect.hxx>
65 #include <simgear/scene/material/EffectGeode.hxx>
66 #include <simgear/scene/material/Technique.hxx>
67 #include <simgear/scene/material/Pass.hxx>
68
69 #include "SGVasiDrawable.hxx"
70
71 using OpenThreads::Mutex;
72 using OpenThreads::ScopedLock;
73
74 using namespace osg;
75 using namespace simgear;
76
77 static Mutex lightMutex;
78
79 namespace
80 {
81 typedef boost::tuple<float, osg::Vec3, float, float, bool> PointParams;
82 typedef std::map<PointParams, observer_ptr<Effect> > EffectMap;
83
84 EffectMap effectMap;
85 }
86
87 Effect* getLightEffect(float size, const Vec3& attenuation,
88                        float minSize, float maxSize, bool directional,
89                        const SGReaderWriterOptions* options)
90 {
91     PointParams pointParams(size, attenuation, minSize, maxSize, directional);
92     ScopedLock<Mutex> lock(lightMutex);
93     ref_ptr<Effect> effect;
94     EffectMap::iterator eitr = effectMap.find(pointParams);
95     if (eitr != effectMap.end())
96     {
97         if (eitr->second.lock(effect))
98             return effect.release();
99     }
100
101     SGPropertyNode_ptr effectProp = new SGPropertyNode;
102     if (directional) {
103         makeChild(effectProp, "inherits-from")->setStringValue("Effects/surface-lights-directional");
104     } else {
105         makeChild(effectProp, "inherits-from")->setStringValue("Effects/surface-lights");
106     }
107
108     SGPropertyNode* params = makeChild(effectProp, "parameters");
109     params->getNode("size",true)->setValue(size);
110     params->getNode("attenuation",true)->getNode("x", true)->setValue(attenuation.x());
111     params->getNode("attenuation",true)->getNode("y", true)->setValue(attenuation.y());
112     params->getNode("attenuation",true)->getNode("z", true)->setValue(attenuation.z());
113     params->getNode("min-size",true)->setValue(minSize);
114     params->getNode("max-size",true)->setValue(maxSize);
115     params->getNode("cull-face",true)->setValue(directional ? "back" : "off");
116     params->getNode("light-directional",true)->setValue(directional);
117
118     effect = makeEffect(effectProp, true, options);
119
120     if (eitr == effectMap.end())
121         effectMap.insert(std::make_pair(pointParams, effect));
122     else
123         eitr->second = effect; // update existing, but empty observer
124     return effect.release();
125 }
126
127
128 osg::Drawable*
129 SGLightFactory::getLightDrawable(const SGLightBin::Light& light)
130 {
131   osg::Vec3Array* vertices = new osg::Vec3Array;
132   osg::Vec4Array* colors = new osg::Vec4Array;
133
134   vertices->push_back(toOsg(light.position));
135   colors->push_back(toOsg(light.color));
136
137   osg::Geometry* geometry = new osg::Geometry;
138   geometry->setDataVariance(osg::Object::STATIC);
139   geometry->setVertexArray(vertices);
140   geometry->setNormalBinding(osg::Geometry::BIND_OFF);
141   geometry->setColorArray(colors);
142   geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
143
144   // Enlarge the bounding box to avoid such light nodes being victim to
145   // small feature culling.
146   geometry->setComputeBoundingBoxCallback(new SGEnlargeBoundingBox(1));
147
148   osg::DrawArrays* drawArrays;
149   drawArrays = new osg::DrawArrays(osg::PrimitiveSet::POINTS,
150                                    0, vertices->size());
151   geometry->addPrimitiveSet(drawArrays);
152   return geometry;
153 }
154
155 osg::Drawable*
156 SGLightFactory::getLightDrawable(const SGDirectionalLightBin::Light& light)
157 {
158   osg::Vec3Array* vertices = new osg::Vec3Array;
159   osg::Vec4Array* colors = new osg::Vec4Array;
160
161   SGVec4f visibleColor(light.color);
162   SGVec4f invisibleColor(visibleColor[0], visibleColor[1],
163                          visibleColor[2], 0);
164   SGVec3f normal = normalize(light.normal);
165   SGVec3f perp1 = perpendicular(normal);
166   SGVec3f perp2 = cross(normal, perp1);
167   SGVec3f position = light.position;
168   vertices->push_back(toOsg(position));
169   vertices->push_back(toOsg(position + perp1));
170   vertices->push_back(toOsg(position + perp2));
171   colors->push_back(toOsg(visibleColor));
172   colors->push_back(toOsg(invisibleColor));
173   colors->push_back(toOsg(invisibleColor));
174
175   osg::Geometry* geometry = new osg::Geometry;
176   geometry->setDataVariance(osg::Object::STATIC);
177   geometry->setVertexArray(vertices);
178   geometry->setNormalBinding(osg::Geometry::BIND_OFF);
179   geometry->setColorArray(colors);
180   geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
181
182   // Enlarge the bounding box to avoid such light nodes being victim to
183   // small feature culling.
184   geometry->setComputeBoundingBoxCallback(new SGEnlargeBoundingBox(1));
185
186   osg::DrawArrays* drawArrays;
187   drawArrays = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES,
188                                    0, vertices->size());
189   geometry->addPrimitiveSet(drawArrays);
190   return geometry;
191 }
192
193 namespace
194 {
195   ref_ptr<StateSet> simpleLightSS;
196 }
197 osg::Drawable*
198 SGLightFactory::getLights(const SGLightBin& lights, unsigned inc, float alphaOff)
199 {
200   if (lights.getNumLights() <= 0)
201     return 0;
202
203   osg::Vec3Array* vertices = new osg::Vec3Array;
204   osg::Vec4Array* colors = new osg::Vec4Array;
205
206   for (unsigned i = 0; i < lights.getNumLights(); i += inc) {
207     vertices->push_back(toOsg(lights.getLight(i).position));
208     SGVec4f color = lights.getLight(i).color;
209     color[3] = SGMiscf::max(0, SGMiscf::min(1, color[3] + alphaOff));
210     colors->push_back(toOsg(color));
211   }
212
213   osg::Geometry* geometry = new osg::Geometry;
214   geometry->setDataVariance(osg::Object::STATIC);
215   geometry->setVertexArray(vertices);
216   geometry->setNormalBinding(osg::Geometry::BIND_OFF);
217   geometry->setColorArray(colors);
218   geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
219
220   osg::DrawArrays* drawArrays;
221   drawArrays = new osg::DrawArrays(osg::PrimitiveSet::POINTS,
222                                    0, vertices->size());
223   geometry->addPrimitiveSet(drawArrays);
224
225   {
226     ScopedLock<Mutex> lock(lightMutex);
227     if (!simpleLightSS.valid()) {
228       StateAttributeFactory *attrFact = StateAttributeFactory::instance();
229       simpleLightSS = new StateSet;
230       simpleLightSS->setRenderBinDetails(POINT_LIGHTS_BIN, "DepthSortedBin");
231       simpleLightSS->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
232       simpleLightSS->setAttributeAndModes(attrFact->getStandardBlendFunc());
233       simpleLightSS->setAttributeAndModes(attrFact->getStandardAlphaFunc());
234     }
235   }
236   geometry->setStateSet(simpleLightSS.get());
237   return geometry;
238 }
239
240
241 osg::Drawable*
242 SGLightFactory::getLights(const SGDirectionalLightBin& lights)
243 {
244   if (lights.getNumLights() <= 0)
245     return 0;
246
247   osg::Vec3Array* vertices = new osg::Vec3Array;
248   osg::Vec4Array* colors = new osg::Vec4Array;
249
250   for (unsigned i = 0; i < lights.getNumLights(); ++i) {
251           SGVec4f visibleColor(lights.getLight(i).color);
252           SGVec4f invisibleColor(visibleColor[0], visibleColor[1],
253                                  visibleColor[2], 0);
254           SGVec3f normal = normalize(lights.getLight(i).normal);
255           SGVec3f perp1 = perpendicular(normal);
256           SGVec3f perp2 = cross(normal, perp1);
257           SGVec3f position = lights.getLight(i).position;
258           vertices->push_back(toOsg(position));
259           vertices->push_back(toOsg(position + perp1));
260           vertices->push_back(toOsg(position + perp2));
261           colors->push_back(toOsg(visibleColor));
262           colors->push_back(toOsg(invisibleColor));
263           colors->push_back(toOsg(invisibleColor));
264   }
265
266   osg::Geometry* geometry = new osg::Geometry;
267   geometry->setDataVariance(osg::Object::STATIC);
268   geometry->setVertexArray(vertices);
269   geometry->setNormalBinding(osg::Geometry::BIND_OFF);
270   geometry->setColorArray(colors, osg::Array::BIND_PER_VERTEX);
271
272
273   //osg::StateSet* stateSet = geometry->getOrCreateStateSet();
274   //stateSet->setRenderBinDetails(POINT_LIGHTS_BIN, "DepthSortedBin");
275   //stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
276
277   osg::DrawArrays* drawArrays;
278   drawArrays = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES,
279                                    0, vertices->size());
280   geometry->addPrimitiveSet(drawArrays);
281   return geometry;
282 }
283
284 static SGVasiDrawable*
285 buildVasi(const SGDirectionalLightBin& lights, const SGVec3f& up,
286        const SGVec4f& red, const SGVec4f& white)
287 {
288   unsigned count = lights.getNumLights();
289   if ( count == 4 ) {
290     SGVasiDrawable* drawable = new SGVasiDrawable(red, white);
291
292     // PAPI configuration
293     // papi D
294     drawable->addLight(lights.getLight(0).position,
295                        lights.getLight(0).normal, up, 3.5);
296     // papi C
297     drawable->addLight(lights.getLight(1).position,
298                        lights.getLight(1).normal, up, 3.167);
299     // papi B
300     drawable->addLight(lights.getLight(2).position,
301                        lights.getLight(2).normal, up, 2.833);
302     // papi A
303     drawable->addLight(lights.getLight(3).position,
304                        lights.getLight(3).normal, up, 2.5);
305     return drawable;
306
307   } else if (count == 6) {
308     SGVasiDrawable* drawable = new SGVasiDrawable(red, white);
309
310     // probably vasi, first 3 are downwind bar (2.5 deg)
311     for (unsigned i = 0; i < 3; ++i)
312       drawable->addLight(lights.getLight(i).position,
313                          lights.getLight(i).normal, up, 2.5);
314     // last 3 are upwind bar (3.0 deg)
315     for (unsigned i = 3; i < 6; ++i)
316       drawable->addLight(lights.getLight(i).position,
317                          lights.getLight(i).normal, up, 3.0);
318     return drawable;
319
320   } else if (count == 12) {
321     SGVasiDrawable* drawable = new SGVasiDrawable(red, white);
322
323     // probably vasi, first 6 are downwind bar (2.5 deg)
324     for (unsigned i = 0; i < 6; ++i)
325       drawable->addLight(lights.getLight(i).position,
326                          lights.getLight(i).normal, up, 2.5);
327     // last 6 are upwind bar (3.0 deg)
328     for (unsigned i = 6; i < 12; ++i)
329       drawable->addLight(lights.getLight(i).position,
330                          lights.getLight(i).normal, up, 3.0);
331     return drawable;
332
333   } else {
334     // fail safe
335     SG_LOG(SG_TERRAIN, SG_ALERT,
336            "unknown vasi/papi configuration, count = " << count);
337     return 0;
338   }
339 }
340
341 osg::Drawable*
342 SGLightFactory::getVasi(const SGVec3f& up, const SGDirectionalLightBin& lights,
343                         const SGVec4f& red, const SGVec4f& white)
344 {
345   SGVasiDrawable* drawable = buildVasi(lights, up, red, white);
346   if (!drawable)
347     return 0;
348
349   osg::StateSet* stateSet = drawable->getOrCreateStateSet();
350   stateSet->setRenderBinDetails(POINT_LIGHTS_BIN, "DepthSortedBin");
351   stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
352
353   osg::BlendFunc* blendFunc = new osg::BlendFunc;
354   stateSet->setAttribute(blendFunc);
355   stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
356
357   osg::AlphaFunc* alphaFunc;
358   alphaFunc = new osg::AlphaFunc(osg::AlphaFunc::GREATER, 0.01);
359   stateSet->setAttribute(alphaFunc);
360   stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON);
361
362   return drawable;
363 }
364
365 osg::Node*
366 SGLightFactory::getSequenced(const SGDirectionalLightBin& lights, const SGReaderWriterOptions* options)
367 {
368   if (lights.getNumLights() <= 0)
369     return 0;
370
371   // generate a repeatable random seed
372   sg_srandom(unsigned(lights.getLight(0).position[0]));
373   float flashTime = 2e-2 + 5e-3*sg_random();
374   osg::Sequence* sequence = new osg::Sequence;
375   sequence->setDefaultTime(flashTime);
376   Effect* effect = getLightEffect(10.0f, osg::Vec3(1.0, 0.0001, 0.00000001),
377                                   6.0f, 10.0f, true, options);
378   for (int i = lights.getNumLights() - 1; 0 <= i; --i) {
379     EffectGeode* egeode = new EffectGeode;
380     egeode->setEffect(effect);
381     egeode->addDrawable(getLightDrawable(lights.getLight(i)));
382     sequence->addChild(egeode, flashTime);
383   }
384   sequence->addChild(new osg::Group, 1 + 1e-1*sg_random());
385   sequence->setInterval(osg::Sequence::LOOP, 0, -1);
386   sequence->setDuration(1.0f, -1);
387   sequence->setMode(osg::Sequence::START);
388   sequence->setSync(true);
389   return sequence;
390 }
391
392 osg::Node*
393 SGLightFactory::getOdal(const SGLightBin& lights, const SGReaderWriterOptions* options)
394 {
395   if (lights.getNumLights() < 2)
396     return 0;
397
398   // generate a repeatable random seed
399   sg_srandom(unsigned(lights.getLight(0).position[0]));
400   float flashTime = 2e-2 + 5e-3*sg_random();
401   osg::Sequence* sequence = new osg::Sequence;
402   sequence->setDefaultTime(flashTime);
403   Effect* effect = getLightEffect(10.0f, osg::Vec3(1.0, 0.0001, 0.00000001),
404                                   6.0, 10.0, false, options);
405   // centerline lights
406   for (int i = lights.getNumLights(); i > 1; --i) {
407     EffectGeode* egeode = new EffectGeode;
408     egeode->setEffect(effect);
409     egeode->addDrawable(getLightDrawable(lights.getLight(i)));
410     sequence->addChild(egeode, flashTime);
411   }
412   // runway end lights
413   osg::Group* group = new osg::Group;
414   for (unsigned i = 0; i < 2; ++i) {
415     EffectGeode* egeode = new EffectGeode;
416     egeode->setEffect(effect);
417     egeode->addDrawable(getLightDrawable(lights.getLight(i)));
418     group->addChild(egeode);
419   }
420   sequence->addChild(group, flashTime);
421
422   // add an extra empty group for a break
423   sequence->addChild(new osg::Group, 2 + 1e-1*sg_random());
424   sequence->setInterval(osg::Sequence::LOOP, 0, -1);
425   sequence->setDuration(1.0f, -1);
426   sequence->setMode(osg::Sequence::START);
427   sequence->setSync(true);
428
429   return sequence;
430 }
431
432 // Blinking hold short line lights
433 osg::Node*
434 SGLightFactory::getHoldShort(const SGDirectionalLightBin& lights, const SGReaderWriterOptions* options)
435 {
436   if (lights.getNumLights() < 2)
437     return 0;
438
439   sg_srandom(unsigned(lights.getLight(0).position[0]));
440   float flashTime = 1 + 0.1 * sg_random();
441   osg::Sequence* sequence = new osg::Sequence;
442
443   // start with lights off
444   sequence->addChild(new osg::Group, flashTime);
445   // ...and increase the lights in steps
446   for (int i = 2; i < 7; i+=2) {
447       Effect* effect = getLightEffect(i, osg::Vec3(1, 0.001, 0.000002),
448                                       0.0f, i, true, options);
449       EffectGeode* egeode = new EffectGeode;
450       for (unsigned int j = 0; j < lights.getNumLights(); ++j) {
451           egeode->addDrawable(getLightDrawable(lights.getLight(j)));
452           egeode->setEffect(effect);
453       }
454       sequence->addChild(egeode, (i==6) ? flashTime : 0.1);
455   }
456
457   sequence->setInterval(osg::Sequence::SWING, 0, -1);
458   sequence->setDuration(1.0f, -1);
459   sequence->setMode(osg::Sequence::START);
460
461   return sequence;
462 }
463
464 // Alternating runway guard lights ("wig-wag")
465 osg::Node*
466 SGLightFactory::getGuard(const SGDirectionalLightBin& lights, const SGReaderWriterOptions* options)
467 {
468   if (lights.getNumLights() < 2)
469     return 0;
470
471   // generate a repeatable random seed
472   sg_srandom(unsigned(lights.getLight(0).position[0]));
473   float flashTime = 1.0f + 1*sg_random();
474   osg::Sequence* sequence = new osg::Sequence;
475   sequence->setDefaultTime(flashTime);
476   Effect* effect = getLightEffect(10.0f, osg::Vec3(1.0, 0.001, 0.000002),
477                                   0.0f, 8.0f, true, options);
478   for (unsigned int i = 0; i < lights.getNumLights(); ++i) {
479     EffectGeode* egeode = new EffectGeode;
480     egeode->setEffect(effect);
481     egeode->addDrawable(getLightDrawable(lights.getLight(i)));
482     sequence->addChild(egeode, flashTime);
483   }
484   sequence->setInterval(osg::Sequence::LOOP, 0, -1);
485   sequence->setDuration(1.0f, -1);
486   sequence->setMode(osg::Sequence::START);
487   sequence->setSync(true);
488   return sequence;
489 }