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