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