00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #ifdef HAVE_CONFIG_H
00024 # include <simgear_config.h>
00025 #endif
00026
00027 #include "pt_lights.hxx"
00028
00029 #include <map>
00030 #include <boost/tuple/tuple_comparison.hpp>
00031
00032 #include <osg/Array>
00033 #include <osg/Geometry>
00034 #include <osg/CullFace>
00035 #include <osg/Geode>
00036 #include <osg/MatrixTransform>
00037 #include <osg/NodeCallback>
00038 #include <osg/NodeVisitor>
00039 #include <osg/Texture2D>
00040 #include <osg/AlphaFunc>
00041 #include <osg/BlendFunc>
00042 #include <osg/TexEnv>
00043 #include <osg/Sequence>
00044 #include <osg/PolygonMode>
00045 #include <osg/Fog>
00046 #include <osg/FragmentProgram>
00047 #include <osg/VertexProgram>
00048 #include <osg/Point>
00049 #include <osg/PointSprite>
00050 #include <osg/Material>
00051 #include <osg/Group>
00052 #include <osg/StateSet>
00053
00054 #include <osgUtil/CullVisitor>
00055
00056 #include <OpenThreads/Mutex>
00057 #include <OpenThreads/ScopedLock>
00058
00059 #include <simgear/math/sg_random.h>
00060 #include <simgear/debug/logstream.hxx>
00061 #include <simgear/scene/util/RenderConstants.hxx>
00062 #include <simgear/scene/util/SGEnlargeBoundingBox.hxx>
00063 #include <simgear/scene/util/StateAttributeFactory.hxx>
00064
00065 #include <simgear/scene/material/Effect.hxx>
00066 #include <simgear/scene/material/EffectGeode.hxx>
00067 #include <simgear/scene/material/Technique.hxx>
00068 #include <simgear/scene/material/Pass.hxx>
00069
00070 #include "SGVasiDrawable.hxx"
00071
00072 using OpenThreads::Mutex;
00073 using OpenThreads::ScopedLock;
00074
00075 using namespace osg;
00076 using namespace simgear;
00077
00078 static void
00079 setPointSpriteImage(unsigned char* data, unsigned log2resolution,
00080 unsigned charsPerPixel)
00081 {
00082 int env_tex_res = (1 << log2resolution);
00083 for (int i = 0; i < env_tex_res; ++i) {
00084 for (int j = 0; j < env_tex_res; ++j) {
00085 int xi = 2*i + 1 - env_tex_res;
00086 int yi = 2*j + 1 - env_tex_res;
00087 if (xi < 0)
00088 xi = -xi;
00089 if (yi < 0)
00090 yi = -yi;
00091
00092 xi -= 1;
00093 yi -= 1;
00094
00095 if (xi < 0)
00096 xi = 0;
00097 if (yi < 0)
00098 yi = 0;
00099
00100 float x = 1.5*xi/(float)(env_tex_res);
00101 float y = 1.5*yi/(float)(env_tex_res);
00102
00103
00104 float dist = sqrt(x*x + y*y);
00105 float bright = SGMiscf::clip(255*(1-dist), 0, 255);
00106 for (unsigned l = 0; l < charsPerPixel; ++l)
00107 data[charsPerPixel*(i*env_tex_res + j) + l] = (unsigned char)bright;
00108 }
00109 }
00110 }
00111
00112 static osg::Image*
00113 getPointSpriteImage(int logResolution)
00114 {
00115 osg::Image* image = new osg::Image;
00116
00117 osg::Image::MipmapDataType mipmapOffsets;
00118 unsigned off = 0;
00119 for (int i = logResolution; 0 <= i; --i) {
00120 unsigned res = 1 << i;
00121 off += res*res;
00122 mipmapOffsets.push_back(off);
00123 }
00124
00125 int env_tex_res = (1 << logResolution);
00126
00127 unsigned char* imageData = new unsigned char[off];
00128 image->setImage(env_tex_res, env_tex_res, 1,
00129 GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, imageData,
00130 osg::Image::USE_NEW_DELETE);
00131 image->setMipmapLevels(mipmapOffsets);
00132
00133 for (int k = logResolution; 0 <= k; --k) {
00134 setPointSpriteImage(image->getMipmapData(logResolution - k), k, 1);
00135 }
00136
00137 return image;
00138 }
00139
00140 static Mutex lightMutex;
00141
00142 static osg::Texture2D*
00143 gen_standard_light_sprite(void)
00144 {
00145
00146 static osg::ref_ptr<osg::Texture2D> texture;
00147 if (texture.valid())
00148 return texture.get();
00149
00150 texture = new osg::Texture2D;
00151 texture->setImage(getPointSpriteImage(6));
00152 texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP);
00153 texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP);
00154
00155 return texture.get();
00156 }
00157
00158 namespace
00159 {
00160 typedef boost::tuple<float, osg::Vec3, float, float, bool> PointParams;
00161 typedef std::map<PointParams, ref_ptr<Effect> > EffectMap;
00162
00163 EffectMap effectMap;
00164
00165 ref_ptr<PolygonMode> polyMode = new PolygonMode(PolygonMode::FRONT,
00166 PolygonMode::POINT);
00167 ref_ptr<PointSprite> pointSprite = new PointSprite;
00168 }
00169
00170 Effect* getLightEffect(float size, const Vec3& attenuation,
00171 float minSize, float maxSize, bool directional)
00172 {
00173 PointParams pointParams(size, attenuation, minSize, maxSize, directional);
00174 ScopedLock<Mutex> lock(lightMutex);
00175 EffectMap::iterator eitr = effectMap.find(pointParams);
00176 if (eitr != effectMap.end())
00177 return eitr->second.get();
00178
00179 Pass *basicPass = new Pass;
00180 basicPass->setRenderBinDetails(POINT_LIGHTS_BIN, "DepthSortedBin");
00181 basicPass->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
00182 StateAttributeFactory *attrFact = StateAttributeFactory::instance();
00183 basicPass->setAttributeAndModes(attrFact->getStandardBlendFunc());
00184 basicPass->setAttributeAndModes(attrFact->getStandardAlphaFunc());
00185 if (directional) {
00186 basicPass->setAttributeAndModes(attrFact->getCullFaceBack());
00187 basicPass->setAttribute(polyMode.get());
00188 }
00189 Pass *attenuationPass = clone(basicPass, CopyOp::SHALLOW_COPY);
00190 osg::Point* point = new osg::Point;
00191 point->setMinSize(minSize);
00192 point->setMaxSize(maxSize);
00193 point->setSize(size);
00194 point->setDistanceAttenuation(attenuation);
00195 attenuationPass->setAttributeAndModes(point);
00196 Pass *spritePass = clone(basicPass, CopyOp::SHALLOW_COPY);
00197 spritePass->setTextureAttributeAndModes(0, pointSprite,
00198 osg::StateAttribute::ON);
00199 Texture2D* texture = gen_standard_light_sprite();
00200 spritePass->setTextureAttribute(0, texture);
00201 spritePass->setTextureMode(0, GL_TEXTURE_2D,
00202 osg::StateAttribute::ON);
00203 spritePass->setTextureAttribute(0, attrFact->getStandardTexEnv());
00204 Pass *combinedPass = clone(spritePass, CopyOp::SHALLOW_COPY);
00205 combinedPass->setAttributeAndModes(point);
00206 Effect* effect = new Effect;
00207 std::vector<std::string> combinedExtensions;
00208 combinedExtensions.push_back("GL_ARB_point_sprite");
00209 combinedExtensions.push_back("GL_ARB_point_parameters");
00210 Technique* combinedTniq = new Technique;
00211 combinedTniq->passes.push_back(combinedPass);
00212 combinedTniq->setGLExtensionsPred(2.0, combinedExtensions);
00213 effect->techniques.push_back(combinedTniq);
00214 std::vector<std::string> spriteExtensions;
00215 spriteExtensions.push_back(combinedExtensions.front());
00216 Technique* spriteTniq = new Technique;
00217 spriteTniq->passes.push_back(spritePass);
00218 spriteTniq->setGLExtensionsPred(2.0, spriteExtensions);
00219 effect->techniques.push_back(spriteTniq);
00220 std::vector<std::string> parameterExtensions;
00221 parameterExtensions.push_back(combinedExtensions.back());
00222 Technique* parameterTniq = new Technique;
00223 parameterTniq->passes.push_back(attenuationPass);
00224 parameterTniq->setGLExtensionsPred(1.4, parameterExtensions);
00225 effect->techniques.push_back(parameterTniq);
00226 Technique* basicTniq = new Technique(true);
00227 basicTniq->passes.push_back(basicPass);
00228 effect->techniques.push_back(basicTniq);
00229 effectMap.insert(std::make_pair(pointParams, effect));
00230 return effect;
00231 }
00232
00233
00234 osg::Drawable*
00235 SGLightFactory::getLightDrawable(const SGLightBin::Light& light)
00236 {
00237 osg::Vec3Array* vertices = new osg::Vec3Array;
00238 osg::Vec4Array* colors = new osg::Vec4Array;
00239
00240 vertices->push_back(toOsg(light.position));
00241 colors->push_back(toOsg(light.color));
00242
00243 osg::Geometry* geometry = new osg::Geometry;
00244 geometry->setVertexArray(vertices);
00245 geometry->setNormalBinding(osg::Geometry::BIND_OFF);
00246 geometry->setColorArray(colors);
00247 geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
00248
00249
00250
00251 geometry->setComputeBoundingBoxCallback(new SGEnlargeBoundingBox(1));
00252
00253 osg::DrawArrays* drawArrays;
00254 drawArrays = new osg::DrawArrays(osg::PrimitiveSet::POINTS,
00255 0, vertices->size());
00256 geometry->addPrimitiveSet(drawArrays);
00257 return geometry;
00258 }
00259
00260 osg::Drawable*
00261 SGLightFactory::getLightDrawable(const SGDirectionalLightBin::Light& light)
00262 {
00263 osg::Vec3Array* vertices = new osg::Vec3Array;
00264 osg::Vec4Array* colors = new osg::Vec4Array;
00265
00266 SGVec4f visibleColor(light.color);
00267 SGVec4f invisibleColor(visibleColor[0], visibleColor[1],
00268 visibleColor[2], 0);
00269 SGVec3f normal = normalize(light.normal);
00270 SGVec3f perp1 = perpendicular(normal);
00271 SGVec3f perp2 = cross(normal, perp1);
00272 SGVec3f position = light.position;
00273 vertices->push_back(toOsg(position));
00274 vertices->push_back(toOsg(position + perp1));
00275 vertices->push_back(toOsg(position + perp2));
00276 colors->push_back(toOsg(visibleColor));
00277 colors->push_back(toOsg(invisibleColor));
00278 colors->push_back(toOsg(invisibleColor));
00279
00280 osg::Geometry* geometry = new osg::Geometry;
00281 geometry->setVertexArray(vertices);
00282 geometry->setNormalBinding(osg::Geometry::BIND_OFF);
00283 geometry->setColorArray(colors);
00284 geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
00285
00286
00287 geometry->setComputeBoundingBoxCallback(new SGEnlargeBoundingBox(1));
00288
00289 osg::DrawArrays* drawArrays;
00290 drawArrays = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES,
00291 0, vertices->size());
00292 geometry->addPrimitiveSet(drawArrays);
00293 return geometry;
00294 }
00295
00296 namespace
00297 {
00298 ref_ptr<StateSet> simpleLightSS;
00299 }
00300 osg::Drawable*
00301 SGLightFactory::getLights(const SGLightBin& lights, unsigned inc, float alphaOff)
00302 {
00303 if (lights.getNumLights() <= 0)
00304 return 0;
00305
00306 osg::Vec3Array* vertices = new osg::Vec3Array;
00307 osg::Vec4Array* colors = new osg::Vec4Array;
00308
00309 for (unsigned i = 0; i < lights.getNumLights(); i += inc) {
00310 vertices->push_back(toOsg(lights.getLight(i).position));
00311 SGVec4f color = lights.getLight(i).color;
00312 color[3] = SGMiscf::max(0, SGMiscf::min(1, color[3] + alphaOff));
00313 colors->push_back(toOsg(color));
00314 }
00315
00316 osg::Geometry* geometry = new osg::Geometry;
00317
00318 geometry->setVertexArray(vertices);
00319 geometry->setNormalBinding(osg::Geometry::BIND_OFF);
00320 geometry->setColorArray(colors);
00321 geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
00322
00323 osg::DrawArrays* drawArrays;
00324 drawArrays = new osg::DrawArrays(osg::PrimitiveSet::POINTS,
00325 0, vertices->size());
00326 geometry->addPrimitiveSet(drawArrays);
00327
00328 {
00329 ScopedLock<Mutex> lock(lightMutex);
00330 if (!simpleLightSS.valid()) {
00331 StateAttributeFactory *attrFact = StateAttributeFactory::instance();
00332 simpleLightSS = new StateSet;
00333 simpleLightSS->setRenderBinDetails(POINT_LIGHTS_BIN, "DepthSortedBin");
00334 simpleLightSS->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
00335 simpleLightSS->setAttributeAndModes(attrFact->getStandardBlendFunc());
00336 simpleLightSS->setAttributeAndModes(attrFact->getStandardAlphaFunc());
00337 }
00338 }
00339 geometry->setStateSet(simpleLightSS.get());
00340 return geometry;
00341 }
00342
00343
00344 osg::Drawable*
00345 SGLightFactory::getLights(const SGDirectionalLightBin& lights)
00346 {
00347 if (lights.getNumLights() <= 0)
00348 return 0;
00349
00350 osg::Vec3Array* vertices = new osg::Vec3Array;
00351 osg::Vec4Array* colors = new osg::Vec4Array;
00352
00353 for (unsigned i = 0; i < lights.getNumLights(); ++i) {
00354 SGVec4f visibleColor(lights.getLight(i).color);
00355 SGVec4f invisibleColor(visibleColor[0], visibleColor[1],
00356 visibleColor[2], 0);
00357 SGVec3f normal = normalize(lights.getLight(i).normal);
00358 SGVec3f perp1 = perpendicular(normal);
00359 SGVec3f perp2 = cross(normal, perp1);
00360 SGVec3f position = lights.getLight(i).position;
00361 vertices->push_back(toOsg(position));
00362 vertices->push_back(toOsg(position + perp1));
00363 vertices->push_back(toOsg(position + perp2));
00364 colors->push_back(toOsg(visibleColor));
00365 colors->push_back(toOsg(invisibleColor));
00366 colors->push_back(toOsg(invisibleColor));
00367 }
00368
00369 osg::Geometry* geometry = new osg::Geometry;
00370
00371 geometry->setVertexArray(vertices);
00372 geometry->setNormalBinding(osg::Geometry::BIND_OFF);
00373 geometry->setColorArray(colors);
00374 geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
00375
00376 osg::DrawArrays* drawArrays;
00377 drawArrays = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES,
00378 0, vertices->size());
00379 geometry->addPrimitiveSet(drawArrays);
00380 return geometry;
00381 }
00382
00383 static SGVasiDrawable*
00384 buildVasi(const SGDirectionalLightBin& lights, const SGVec3f& up,
00385 const SGVec4f& red, const SGVec4f& white)
00386 {
00387 unsigned count = lights.getNumLights();
00388 if ( count == 4 ) {
00389 SGVasiDrawable* drawable = new SGVasiDrawable(red, white);
00390
00391
00392
00393 drawable->addLight(lights.getLight(0).position,
00394 lights.getLight(0).normal, up, 3.5);
00395
00396 drawable->addLight(lights.getLight(1).position,
00397 lights.getLight(1).normal, up, 3.167);
00398
00399 drawable->addLight(lights.getLight(2).position,
00400 lights.getLight(2).normal, up, 2.833);
00401
00402 drawable->addLight(lights.getLight(3).position,
00403 lights.getLight(3).normal, up, 2.5);
00404 return drawable;
00405 }
00406 else if (count == 12) {
00407 SGVasiDrawable* drawable = new SGVasiDrawable(red, white);
00408
00409
00410 for (unsigned i = 0; i < 6; ++i)
00411 drawable->addLight(lights.getLight(i).position,
00412 lights.getLight(i).normal, up, 2.5);
00413
00414 for (unsigned i = 6; i < 12; ++i)
00415 drawable->addLight(lights.getLight(i).position,
00416 lights.getLight(i).normal, up, 3.0);
00417
00418 return drawable;
00419 } else {
00420
00421 SG_LOG(SG_TERRAIN, SG_ALERT,
00422 "unknown vasi/papi configuration, count = " << count);
00423 return 0;
00424 }
00425 }
00426
00427 osg::Drawable*
00428 SGLightFactory::getVasi(const SGVec3f& up, const SGDirectionalLightBin& lights,
00429 const SGVec4f& red, const SGVec4f& white)
00430 {
00431 SGVasiDrawable* drawable = buildVasi(lights, up, red, white);
00432 if (!drawable)
00433 return 0;
00434
00435 osg::StateSet* stateSet = drawable->getOrCreateStateSet();
00436 stateSet->setRenderBinDetails(POINT_LIGHTS_BIN, "DepthSortedBin");
00437 stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
00438
00439 osg::BlendFunc* blendFunc = new osg::BlendFunc;
00440 stateSet->setAttribute(blendFunc);
00441 stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
00442
00443 osg::AlphaFunc* alphaFunc;
00444 alphaFunc = new osg::AlphaFunc(osg::AlphaFunc::GREATER, 0.01);
00445 stateSet->setAttribute(alphaFunc);
00446 stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON);
00447
00448 return drawable;
00449 }
00450
00451 osg::Node*
00452 SGLightFactory::getSequenced(const SGDirectionalLightBin& lights)
00453 {
00454 if (lights.getNumLights() <= 0)
00455 return 0;
00456
00457
00458 sg_srandom(unsigned(lights.getLight(0).position[0]));
00459 float flashTime = 2e-2 + 5e-3*sg_random();
00460 osg::Sequence* sequence = new osg::Sequence;
00461 sequence->setDefaultTime(flashTime);
00462 Effect* effect = getLightEffect(10.0f, osg::Vec3(1.0, 0.0001, 0.00000001),
00463 6.0f, 10.0f, true);
00464 for (int i = lights.getNumLights() - 1; 0 <= i; --i) {
00465 EffectGeode* egeode = new EffectGeode;
00466 egeode->setEffect(effect);
00467 egeode->addDrawable(getLightDrawable(lights.getLight(i)));
00468 sequence->addChild(egeode, flashTime);
00469 }
00470 sequence->addChild(new osg::Group, 1 + 1e-1*sg_random());
00471 sequence->setInterval(osg::Sequence::LOOP, 0, -1);
00472 sequence->setDuration(1.0f, -1);
00473 sequence->setMode(osg::Sequence::START);
00474 sequence->setSync(true);
00475 return sequence;
00476 }
00477
00478 osg::Node*
00479 SGLightFactory::getOdal(const SGLightBin& lights)
00480 {
00481 if (lights.getNumLights() < 2)
00482 return 0;
00483
00484
00485 sg_srandom(unsigned(lights.getLight(0).position[0]));
00486 float flashTime = 2e-2 + 5e-3*sg_random();
00487 osg::Sequence* sequence = new osg::Sequence;
00488 sequence->setDefaultTime(flashTime);
00489 Effect* effect = getLightEffect(10.0f, osg::Vec3(1.0, 0.0001, 0.00000001),
00490 6.0, 10.0, false);
00491
00492 for (int i = lights.getNumLights() - 1; 2 <= i; --i) {
00493 EffectGeode* egeode = new EffectGeode;
00494 egeode->setEffect(effect);
00495 egeode->addDrawable(getLightDrawable(lights.getLight(i)));
00496 sequence->addChild(egeode, flashTime);
00497 }
00498
00499 osg::Group* group = new osg::Group;
00500 for (unsigned i = 0; i < 2; ++i) {
00501 EffectGeode* egeode = new EffectGeode;
00502 egeode->setEffect(effect);
00503 egeode->addDrawable(getLightDrawable(lights.getLight(i)));
00504 group->addChild(egeode);
00505 }
00506 sequence->addChild(group, flashTime);
00507
00508
00509 sequence->addChild(new osg::Group, 9 + 1e-1*sg_random());
00510 sequence->setInterval(osg::Sequence::LOOP, 0, -1);
00511 sequence->setDuration(1.0f, -1);
00512 sequence->setMode(osg::Sequence::START);
00513 sequence->setSync(true);
00514
00515 return sequence;
00516 }