X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fscene%2Ftgdb%2Fpt_lights.cxx;h=bfe3c5ae963544aaab92c694afc79d79cdce94e3;hb=9cbbe5559844317f44744788ddb308101a1e75e9;hp=5b3dcc2d4346a99e29eda693803c813893c81cc5;hpb=7fc8c026884b2d0a1b683765c089a9bef5ac47c8;p=simgear.git diff --git a/simgear/scene/tgdb/pt_lights.cxx b/simgear/scene/tgdb/pt_lights.cxx index 5b3dcc2d..bfe3c5ae 100644 --- a/simgear/scene/tgdb/pt_lights.cxx +++ b/simgear/scene/tgdb/pt_lights.cxx @@ -16,657 +16,501 @@ // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software -// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // $Id$ - -#include - -#include -#include - -#include "vasi.hxx" +#ifdef HAVE_CONFIG_H +# include +#endif #include "pt_lights.hxx" - -// strobe pre-draw (we want a larger point size) -static int StrobePreDraw( ssgEntity *e ) { - glPushAttrib( GL_POINT_BIT ); - glPointSize(4.0); - glEnable(GL_POINT_SMOOTH); - - return true; -} - -// strobe post-draw (we want a larger point size) -static int StrobePostDraw( ssgEntity *e ) { - glPopAttrib(); - - return true; +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "SGVasiDrawable.hxx" + +using OpenThreads::Mutex; +using OpenThreads::ScopedLock; + +using namespace osg; +using namespace simgear; + +static void +setPointSpriteImage(unsigned char* data, unsigned log2resolution, + unsigned charsPerPixel) +{ + int env_tex_res = (1 << log2resolution); + for (int i = 0; i < env_tex_res; ++i) { + for (int j = 0; j < env_tex_res; ++j) { + int xi = 2*i + 1 - env_tex_res; + int yi = 2*j + 1 - env_tex_res; + if (xi < 0) + xi = -xi; + if (yi < 0) + yi = -yi; + + xi -= 1; + yi -= 1; + + if (xi < 0) + xi = 0; + if (yi < 0) + yi = 0; + + float x = 1.5*xi/(float)(env_tex_res); + float y = 1.5*yi/(float)(env_tex_res); + // float x = 2*xi/(float)(env_tex_res); + // float y = 2*yi/(float)(env_tex_res); + float dist = sqrt(x*x + y*y); + float bright = SGMiscf::clip(255*(1-dist), 0, 255); + for (unsigned l = 0; l < charsPerPixel; ++l) + data[charsPerPixel*(i*env_tex_res + j) + l] = (unsigned char)bright; + } + } } - -// vasi pre-draw (we want a larger point size) -static int VASIPreDraw( ssgEntity *e ) { - glPushAttrib( GL_POINT_BIT ); - glPointSize(2.0); - glEnable(GL_POINT_SMOOTH); - - return true; +static osg::Image* +getPointSpriteImage(int logResolution) +{ + osg::Image* image = new osg::Image; + + osg::Image::MipmapDataType mipmapOffsets; + unsigned off = 0; + for (int i = logResolution; 0 <= i; --i) { + unsigned res = 1 << i; + off += res*res; + mipmapOffsets.push_back(off); + } + + int env_tex_res = (1 << logResolution); + + unsigned char* imageData = new unsigned char[off]; + image->setImage(env_tex_res, env_tex_res, 1, + GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, imageData, + osg::Image::USE_NEW_DELETE); + image->setMipmapLevels(mipmapOffsets); + + for (int k = logResolution; 0 <= k; --k) { + setPointSpriteImage(image->getMipmapData(logResolution - k), k, 1); + } + + return image; } -// vasi post-draw (we want a larger point size) -static int VASIPostDraw( ssgEntity *e ) { - glPopAttrib(); +static Mutex lightMutex; - return true; +static osg::Texture2D* +gen_standard_light_sprite(void) +{ + // Always called from when the lightMutex is already taken + static osg::ref_ptr texture; + if (texture.valid()) + return texture.get(); + + texture = new osg::Texture2D; + texture->setImage(getPointSpriteImage(6)); + texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP); + texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP); + + return texture.get(); } +namespace +{ +typedef boost::tuple PointParams; +typedef std::map > EffectMap; -// Generate a directional light -ssgLeaf *sgMakeDirectionalLight( sgVec3 pt, sgVec3 dir, sgVec3 up, - const SGMaterial *mat ) { - - // calculate a vector perpendicular to dir and up - sgVec3 perp; - sgVectorProductVec3( perp, dir, up ); - - ssgVertexArray *vl = new ssgVertexArray( 3 ); - ssgNormalArray *nl = new ssgNormalArray( 3 ); - ssgColourArray *cl = new ssgColourArray( 3 ); - - // front face - sgVec3 tmp3; - sgCopyVec3( tmp3, pt ); - vl->add( tmp3 ); - sgAddVec3( tmp3, up ); - vl->add( tmp3 ); - sgAddVec3( tmp3, perp ); - vl->add( tmp3 ); - // sgSubVec3( tmp3, up ); - // vl->add( tmp3 ); - - nl->add( dir ); - nl->add( dir ); - nl->add( dir ); - // nl->add( dir ); - - sgVec4 color; - sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 ); - cl->add( color ); - sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 ); - cl->add( color ); - cl->add( color ); - // cl->add( color ); - - /* - // temporarily do back face - sgCopyVec3( tmp3, pt ); - vl->add( tmp3 ); - sgAddVec3( tmp3, up ); - vl->add( tmp3 ); - sgAddVec3( tmp3, perp ); - vl->add( tmp3 ); - - sgNegateVec3( dir ); - nl->add( dir ); - nl->add( dir ); - nl->add( dir ); - - sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 ); - cl->add( color ); - sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 ); - cl->add( color ); - cl->add( color ); - */ - - /* ssgTexCoordArray *tl = new ssgTexCoordArray( 4 ); - sgVec2 tmp2; - sgSetVec2( tmp2, 0.0, 0.0 ); - tl->add( tmp2 ); - sgSetVec2( tmp2, 1.0, 0.0 ); - tl->add( tmp2 ); - sgSetVec2( tmp2, 1.0, 1.0 ); - tl->add( tmp2 ); - sgSetVec2( tmp2, 0.0, 1.0 ); - tl->add( tmp2 ); */ - - ssgLeaf *leaf = - new ssgVtxTable ( GL_TRIANGLES, vl, nl, NULL, cl ); - - if ( mat != NULL ) { - leaf->setState( mat->get_state() ); - } else { - SG_LOG( SG_TERRAIN, SG_ALERT, "Warning: mat = NULL" ); - } +EffectMap effectMap; - return leaf; +ref_ptr polyMode = new PolygonMode(PolygonMode::FRONT, + PolygonMode::POINT); +ref_ptr pointSprite = new PointSprite; } - -static void calc_center_point( const point_list &nodes, - const int_list &pnt_i, - sgVec3 result ) { - sgVec3 pt; - sgSetVec3( pt, nodes[pnt_i[0]][0], nodes[pnt_i[0]][1], nodes[pnt_i[0]][2] ); - - double minx = pt[0]; - double maxx = pt[0]; - double miny = pt[1]; - double maxy = pt[1]; - double minz = pt[2]; - double maxz = pt[2]; - - for ( unsigned int i = 0; i < pnt_i.size(); ++i ) { - sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1], - nodes[pnt_i[i]][2] ); - if ( pt[0] < minx ) { minx = pt[0]; } - if ( pt[0] > maxx ) { minx = pt[0]; } - if ( pt[1] < miny ) { miny = pt[1]; } - if ( pt[1] > maxy ) { miny = pt[1]; } - if ( pt[2] < minz ) { minz = pt[2]; } - if ( pt[2] > maxz ) { minz = pt[2]; } +Effect* getLightEffect(float size, const Vec3& attenuation, + float minSize, float maxSize, bool directional) +{ + PointParams pointParams(size, attenuation, minSize, maxSize, directional); + ScopedLock lock(lightMutex); + EffectMap::iterator eitr = effectMap.find(pointParams); + if (eitr != effectMap.end()) + return eitr->second.get(); + // Basic stuff; no sprite or attenuation support + Pass *basicPass = new Pass; + basicPass->setRenderBinDetails(POINT_LIGHTS_BIN, "DepthSortedBin"); + basicPass->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + StateAttributeFactory *attrFact = StateAttributeFactory::instance(); + basicPass->setAttributeAndModes(attrFact->getStandardBlendFunc()); + basicPass->setAttributeAndModes(attrFact->getStandardAlphaFunc()); + if (directional) { + basicPass->setAttributeAndModes(attrFact->getCullFaceBack()); + basicPass->setAttribute(polyMode.get()); } - - sgSetVec3( result, (minx + maxx) / 2.0, (miny + maxy) / 2.0, - (minz + maxz) / 2.0 ); + Pass *attenuationPass = clone(basicPass, CopyOp::SHALLOW_COPY); + osg::Point* point = new osg::Point; + point->setMinSize(minSize); + point->setMaxSize(maxSize); + point->setSize(size); + point->setDistanceAttenuation(attenuation); + attenuationPass->setAttributeAndModes(point); + Pass *spritePass = clone(basicPass, CopyOp::SHALLOW_COPY); + spritePass->setTextureAttributeAndModes(0, pointSprite, + osg::StateAttribute::ON); + Texture2D* texture = gen_standard_light_sprite(); + spritePass->setTextureAttribute(0, texture); + spritePass->setTextureMode(0, GL_TEXTURE_2D, + osg::StateAttribute::ON); + spritePass->setTextureAttribute(0, attrFact->getStandardTexEnv()); + Pass *combinedPass = clone(spritePass, CopyOp::SHALLOW_COPY); + combinedPass->setAttributeAndModes(point); + Effect* effect = new Effect; + std::vector combinedExtensions; + combinedExtensions.push_back("GL_ARB_point_sprite"); + combinedExtensions.push_back("GL_ARB_point_parameters"); + Technique* combinedTniq = new Technique; + combinedTniq->passes.push_back(combinedPass); + combinedTniq->setGLExtensionsPred(2.0, combinedExtensions); + effect->techniques.push_back(combinedTniq); + std::vector spriteExtensions; + spriteExtensions.push_back(combinedExtensions.front()); + Technique* spriteTniq = new Technique; + spriteTniq->passes.push_back(spritePass); + spriteTniq->setGLExtensionsPred(2.0, spriteExtensions); + effect->techniques.push_back(spriteTniq); + std::vector parameterExtensions; + parameterExtensions.push_back(combinedExtensions.back()); + Technique* parameterTniq = new Technique; + parameterTniq->passes.push_back(attenuationPass); + parameterTniq->setGLExtensionsPred(1.4, parameterExtensions); + effect->techniques.push_back(parameterTniq); + Technique* basicTniq = new Technique(true); + basicTniq->passes.push_back(basicPass); + effect->techniques.push_back(basicTniq); + effectMap.insert(std::make_pair(pointParams, effect)); + return effect; } -static ssgTransform *gen_dir_light_group( const point_list &nodes, - const point_list &normals, - const int_list &pnt_i, - const int_list &nml_i, - const SGMaterial *mat, - sgVec3 up, bool vertical ) +osg::Drawable* +SGLightFactory::getLightDrawable(const SGLightBin::Light& light) { - sgVec3 center; - calc_center_point( nodes, pnt_i, center ); - // cout << center[0] << "," << center[1] << "," << center[2] << endl; - - - // find a vector perpendicular to the normal. - sgVec3 perp1; - if ( !vertical ) { - // normal isn't vertical so we can use up as our first vector - sgNormalizeVec3( perp1, up ); - } else { - // normal is vertical so we have to work a bit harder to - // determine our first vector - sgVec3 pt1, pt2; - sgSetVec3( pt1, nodes[pnt_i[0]][0], nodes[pnt_i[0]][1], - nodes[pnt_i[0]][2] ); - sgSetVec3( pt2, nodes[pnt_i[1]][0], nodes[pnt_i[1]][1], - nodes[pnt_i[1]][2] ); - - sgSubVec3( perp1, pt2, pt1 ); - sgNormalizeVec3( perp1 ); - } - - ssgVertexArray *vl = new ssgVertexArray( 3 * pnt_i.size() ); - ssgNormalArray *nl = new ssgNormalArray( 3 * pnt_i.size() ); - ssgColourArray *cl = new ssgColourArray( 3 * pnt_i.size() ); - - unsigned int i; - sgVec3 pt, normal; - for ( i = 0; i < pnt_i.size(); ++i ) { - sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1], - nodes[pnt_i[i]][2] ); - sgSubVec3( pt, center ); - sgSetVec3( normal, normals[nml_i[i]][0], normals[nml_i[i]][1], - normals[nml_i[i]][2] ); - - // calculate a vector perpendicular to dir and up - sgVec3 perp2; - sgVectorProductVec3( perp2, normal, perp1 ); - - // front face - sgVec3 tmp3; - sgCopyVec3( tmp3, pt ); - vl->add( tmp3 ); - sgAddVec3( tmp3, perp1 ); - vl->add( tmp3 ); - sgAddVec3( tmp3, perp2 ); - vl->add( tmp3 ); - // sgSubVec3( tmp3, perp1 ); - // vl->add( tmp3 ); - - nl->add( normal ); - nl->add( normal ); - nl->add( normal ); - // nl->add( normal ); - - sgVec4 color; - sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 ); - cl->add( color ); - sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 ); - cl->add( color ); - cl->add( color ); - // cl->add( color ); - } - - ssgLeaf *leaf = - new ssgVtxTable ( GL_TRIANGLES, vl, nl, NULL, cl ); - - if ( mat != NULL ) { - leaf->setState( mat->get_state() ); - } else { - SG_LOG( SG_TERRAIN, SG_ALERT, "Warning: material = NULL" ); - } - - // put an LOD on each lighting component - ssgRangeSelector *lod = new ssgRangeSelector; - lod->setRange( 0, SG_ZERO ); - lod->setRange( 1, 20000 ); - lod->addKid( leaf ); - - // create the transformation. - sgCoord coord; - sgSetCoord( &coord, center[0], center[1], center[2], 0.0, 0.0, 0.0 ); - ssgTransform *trans = new ssgTransform; - trans->setTransform( &coord ); - trans->addKid( lod ); - - return trans; + osg::Vec3Array* vertices = new osg::Vec3Array; + osg::Vec4Array* colors = new osg::Vec4Array; + + vertices->push_back(toOsg(light.position)); + colors->push_back(toOsg(light.color)); + + osg::Geometry* geometry = new osg::Geometry; + geometry->setVertexArray(vertices); + geometry->setNormalBinding(osg::Geometry::BIND_OFF); + geometry->setColorArray(colors); + geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX); + + // Enlarge the bounding box to avoid such light nodes being victim to + // small feature culling. + geometry->setComputeBoundingBoxCallback(new SGEnlargeBoundingBox(1)); + + osg::DrawArrays* drawArrays; + drawArrays = new osg::DrawArrays(osg::PrimitiveSet::POINTS, + 0, vertices->size()); + geometry->addPrimitiveSet(drawArrays); + return geometry; } - -static ssgTransform *gen_reil_lights( const point_list &nodes, - const point_list &normals, - const int_list &pnt_i, - const int_list &nml_i, - SGMaterialLib *matlib, - sgVec3 up ) +osg::Drawable* +SGLightFactory::getLightDrawable(const SGDirectionalLightBin::Light& light) { - sgVec3 center; - calc_center_point( nodes, pnt_i, center ); - // cout << center[0] << "," << center[1] << "," << center[2] << endl; - - sgVec3 nup; - sgNormalizeVec3( nup, up ); - - ssgVertexArray *vl = new ssgVertexArray( 3 * pnt_i.size() ); - ssgNormalArray *nl = new ssgNormalArray( 3 * pnt_i.size() ); - ssgColourArray *cl = new ssgColourArray( 3 * pnt_i.size() ); - - unsigned int i; - sgVec3 pt, normal; - for ( i = 0; i < pnt_i.size(); ++i ) { - sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1], - nodes[pnt_i[i]][2] ); - sgSubVec3( pt, center ); - sgSetVec3( normal, normals[nml_i[i]][0], normals[nml_i[i]][1], - normals[nml_i[i]][2] ); - - // calculate a vector perpendicular to dir and up - sgVec3 perp; - sgVectorProductVec3( perp, normal, nup ); - - // front face - sgVec3 tmp3; - sgCopyVec3( tmp3, pt ); - vl->add( tmp3 ); - sgAddVec3( tmp3, nup ); - vl->add( tmp3 ); - sgAddVec3( tmp3, perp ); - vl->add( tmp3 ); - // sgSubVec3( tmp3, nup ); - // vl->add( tmp3 ); - - nl->add( normal ); - nl->add( normal ); - nl->add( normal ); - // nl->add( normal ); - - sgVec4 color; - sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 ); - cl->add( color ); - sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 ); - cl->add( color ); - cl->add( color ); - // cl->add( color ); - } - - ssgLeaf *leaf = - new ssgVtxTable ( GL_TRIANGLES, vl, nl, NULL, cl ); - - SGMaterial *mat = matlib->find( "RWY_WHITE_LIGHTS" ); - - if ( mat != NULL ) { - leaf->setState( mat->get_state() ); - } else { - SG_LOG( SG_TERRAIN, SG_ALERT, - "Warning: can't find material = RWY_WHITE_LIGHTS" ); - } - - leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw ); - leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw ); - - ssgTimedSelector *reil = new ssgTimedSelector; - - // need to add this twice to work around an ssg bug - reil->addKid( leaf ); - reil->addKid( leaf ); - - reil->setDuration( 60 ); - reil->setLimits( 0, 2 ); - reil->setMode( SSG_ANIM_SHUTTLE ); - reil->control( SSG_ANIM_START ); - - // put an LOD on each lighting component - ssgRangeSelector *lod = new ssgRangeSelector; - lod->setRange( 0, SG_ZERO ); - lod->setRange( 1, 12000 ); - lod->addKid( reil ); - - // create the transformation. - sgCoord coord; - sgSetCoord( &coord, center[0], center[1], center[2], 0.0, 0.0, 0.0 ); - ssgTransform *trans = new ssgTransform; - trans->setTransform( &coord ); - trans->addKid( lod ); - - return trans; + osg::Vec3Array* vertices = new osg::Vec3Array; + osg::Vec4Array* colors = new osg::Vec4Array; + + SGVec4f visibleColor(light.color); + SGVec4f invisibleColor(visibleColor[0], visibleColor[1], + visibleColor[2], 0); + SGVec3f normal = normalize(light.normal); + SGVec3f perp1 = perpendicular(normal); + SGVec3f perp2 = cross(normal, perp1); + SGVec3f position = light.position; + vertices->push_back(toOsg(position)); + vertices->push_back(toOsg(position + perp1)); + vertices->push_back(toOsg(position + perp2)); + colors->push_back(toOsg(visibleColor)); + colors->push_back(toOsg(invisibleColor)); + colors->push_back(toOsg(invisibleColor)); + + osg::Geometry* geometry = new osg::Geometry; + geometry->setVertexArray(vertices); + geometry->setNormalBinding(osg::Geometry::BIND_OFF); + geometry->setColorArray(colors); + geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX); + // Enlarge the bounding box to avoid such light nodes being victim to + // small feature culling. + geometry->setComputeBoundingBoxCallback(new SGEnlargeBoundingBox(1)); + + osg::DrawArrays* drawArrays; + drawArrays = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES, + 0, vertices->size()); + geometry->addPrimitiveSet(drawArrays); + return geometry; } - -static ssgTransform *gen_odals_lights( const point_list &nodes, - const point_list &normals, - const int_list &pnt_i, - const int_list &nml_i, - SGMaterialLib *matlib, - sgVec3 up ) +namespace { - sgVec3 center; - calc_center_point( nodes, pnt_i, center ); - // cout << center[0] << "," << center[1] << "," << center[2] << endl; - - ssgTimedSelector *odals = new ssgTimedSelector; - - sgVec4 color; - sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 ); - - // we don't want directional lights here - SGMaterial *mat = matlib->find( "GROUND_LIGHTS" ); - if ( mat == NULL ) { - SG_LOG( SG_TERRAIN, SG_ALERT, - "Warning: can't material = GROUND_LIGHTS" ); - } - - // center line strobes - int i; - sgVec3 pt; - for ( i = (int)pnt_i.size() - 1; i >= 2; --i ) { - ssgVertexArray *vl = new ssgVertexArray( 1 ); - ssgColourArray *cl = new ssgColourArray( 1 ); - - sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1], - nodes[pnt_i[i]][2] ); - sgSubVec3( pt, center ); - vl->add( pt ); - - cl->add( color ); - - ssgLeaf *leaf = - new ssgVtxTable ( GL_POINTS, vl, NULL, NULL, cl ); - - leaf->setState( mat->get_state() ); - leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw ); - leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw ); - - odals->addKid( leaf ); - } - - // runway end strobes - ssgVertexArray *vl = new ssgVertexArray( 2 ); - ssgColourArray *cl = new ssgColourArray( 2 ); - - sgSetVec3( pt, nodes[pnt_i[0]][0], nodes[pnt_i[0]][1], - nodes[pnt_i[0]][2] ); - sgSubVec3( pt, center ); - vl->add( pt ); - cl->add( color ); - - sgSetVec3( pt, nodes[pnt_i[1]][0], nodes[pnt_i[1]][1], - nodes[pnt_i[1]][2] ); - sgSubVec3( pt, center ); - vl->add( pt ); - cl->add( color ); - - ssgLeaf *leaf = - new ssgVtxTable ( GL_POINTS, vl, NULL, NULL, cl ); - - leaf->setState( mat->get_state() ); - leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw ); - leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw ); - - odals->addKid( leaf ); - - // setup animition - - odals->setDuration( 10 ); - odals->setLimits( 0, pnt_i.size() - 1 ); - odals->setMode( SSG_ANIM_SHUTTLE ); - odals->control( SSG_ANIM_START ); - - // put an LOD on each lighting component - ssgRangeSelector *lod = new ssgRangeSelector; - lod->setRange( 0, SG_ZERO ); - lod->setRange( 1, 12000 ); - lod->addKid( odals ); - - // create the transformation. - sgCoord coord; - sgSetCoord( &coord, center[0], center[1], center[2], 0.0, 0.0, 0.0 ); - ssgTransform *trans = new ssgTransform; - trans->setTransform( &coord ); - trans->addKid( lod ); - - return trans; + ref_ptr simpleLightSS; } - - -static ssgTransform *gen_rabbit_lights( const point_list &nodes, - const point_list &normals, - const int_list &pnt_i, - const int_list &nml_i, - SGMaterialLib *matlib, - sgVec3 up ) +osg::Drawable* +SGLightFactory::getLights(const SGLightBin& lights, unsigned inc, float alphaOff) { - sgVec3 center; - calc_center_point( nodes, pnt_i, center ); - // cout << center[0] << "," << center[1] << "," << center[2] << endl; - - sgVec3 nup; - sgNormalizeVec3( nup, up ); - - ssgTimedSelector *rabbit = new ssgTimedSelector; - - SGMaterial *mat = matlib->find( "RWY_WHITE_LIGHTS" ); - if ( mat == NULL ) { - SG_LOG( SG_TERRAIN, SG_ALERT, - "Warning: can't material = RWY_WHITE_LIGHTS" ); - } - - int i; - sgVec3 pt, normal; - for ( i = (int)pnt_i.size() - 1; i >= 0; --i ) { - ssgVertexArray *vl = new ssgVertexArray( 3 ); - ssgNormalArray *nl = new ssgNormalArray( 3 ); - ssgColourArray *cl = new ssgColourArray( 3 ); - - sgSetVec3( pt, nodes[pnt_i[i]][0], nodes[pnt_i[i]][1], - nodes[pnt_i[i]][2] ); - sgSubVec3( pt, center ); - - sgSetVec3( normal, normals[nml_i[i]][0], normals[nml_i[i]][1], - normals[nml_i[i]][2] ); - - // calculate a vector perpendicular to dir and up - sgVec3 perp; - sgVectorProductVec3( perp, normal, nup ); - - // front face - sgVec3 tmp3; - sgCopyVec3( tmp3, pt ); - vl->add( tmp3 ); - sgAddVec3( tmp3, nup ); - vl->add( tmp3 ); - sgAddVec3( tmp3, perp ); - vl->add( tmp3 ); - // sgSubVec3( tmp3, nup ); - // vl->add( tmp3 ); - - nl->add( normal ); - nl->add( normal ); - nl->add( normal ); - // nl->add( normal ); - - sgVec4 color; - sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 ); - cl->add( color ); - sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 ); - cl->add( color ); - cl->add( color ); - // cl->add( color ); - - ssgLeaf *leaf = - new ssgVtxTable ( GL_TRIANGLES, vl, nl, NULL, cl ); - - leaf->setState( mat->get_state() ); - leaf->setCallback( SSG_CALLBACK_PREDRAW, StrobePreDraw ); - leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw ); - - rabbit->addKid( leaf ); + if (lights.getNumLights() <= 0) + return 0; + + osg::Vec3Array* vertices = new osg::Vec3Array; + osg::Vec4Array* colors = new osg::Vec4Array; + + for (unsigned i = 0; i < lights.getNumLights(); i += inc) { + vertices->push_back(toOsg(lights.getLight(i).position)); + SGVec4f color = lights.getLight(i).color; + color[3] = SGMiscf::max(0, SGMiscf::min(1, color[3] + alphaOff)); + colors->push_back(toOsg(color)); + } + + osg::Geometry* geometry = new osg::Geometry; + + geometry->setVertexArray(vertices); + geometry->setNormalBinding(osg::Geometry::BIND_OFF); + geometry->setColorArray(colors); + geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX); + + osg::DrawArrays* drawArrays; + drawArrays = new osg::DrawArrays(osg::PrimitiveSet::POINTS, + 0, vertices->size()); + geometry->addPrimitiveSet(drawArrays); + + { + ScopedLock lock(lightMutex); + if (!simpleLightSS.valid()) { + StateAttributeFactory *attrFact = StateAttributeFactory::instance(); + simpleLightSS = new StateSet; + simpleLightSS->setRenderBinDetails(POINT_LIGHTS_BIN, "DepthSortedBin"); + simpleLightSS->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + simpleLightSS->setAttributeAndModes(attrFact->getStandardBlendFunc()); + simpleLightSS->setAttributeAndModes(attrFact->getStandardAlphaFunc()); } - - rabbit->setDuration( 10 ); - rabbit->setLimits( 0, pnt_i.size() - 1 ); - rabbit->setMode( SSG_ANIM_SHUTTLE ); - rabbit->control( SSG_ANIM_START ); - - // put an LOD on each lighting component - ssgRangeSelector *lod = new ssgRangeSelector; - lod->setRange( 0, SG_ZERO ); - lod->setRange( 1, 12000 ); - lod->addKid( rabbit ); - - // create the transformation. - sgCoord coord; - sgSetCoord( &coord, center[0], center[1], center[2], 0.0, 0.0, 0.0 ); - ssgTransform *trans = new ssgTransform; - trans->setTransform( &coord ); - trans->addKid( lod ); - - return trans; + } + geometry->setStateSet(simpleLightSS.get()); + return geometry; } -#if 0 // debugging infrastructure -// Generate a normal line -static ssgLeaf *gen_normal_line( SGMaterialLib *matlib, - sgVec3 pt, sgVec3 dir, sgVec3 up ) +osg::Drawable* +SGLightFactory::getLights(const SGDirectionalLightBin& lights) { + if (lights.getNumLights() <= 0) + return 0; + + osg::Vec3Array* vertices = new osg::Vec3Array; + osg::Vec4Array* colors = new osg::Vec4Array; + + for (unsigned i = 0; i < lights.getNumLights(); ++i) { + SGVec4f visibleColor(lights.getLight(i).color); + SGVec4f invisibleColor(visibleColor[0], visibleColor[1], + visibleColor[2], 0); + SGVec3f normal = normalize(lights.getLight(i).normal); + SGVec3f perp1 = perpendicular(normal); + SGVec3f perp2 = cross(normal, perp1); + SGVec3f position = lights.getLight(i).position; + vertices->push_back(toOsg(position)); + vertices->push_back(toOsg(position + perp1)); + vertices->push_back(toOsg(position + perp2)); + colors->push_back(toOsg(visibleColor)); + colors->push_back(toOsg(invisibleColor)); + colors->push_back(toOsg(invisibleColor)); + } + + osg::Geometry* geometry = new osg::Geometry; + + geometry->setVertexArray(vertices); + geometry->setNormalBinding(osg::Geometry::BIND_OFF); + geometry->setColorArray(colors); + geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX); + + osg::DrawArrays* drawArrays; + drawArrays = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES, + 0, vertices->size()); + geometry->addPrimitiveSet(drawArrays); + return geometry; +} - ssgVertexArray *vl = new ssgVertexArray( 3 ); - ssgColourArray *cl = new ssgColourArray( 3 ); - - sgVec3 tmp3; - sgCopyVec3( tmp3, pt ); - vl->add( tmp3 ); - sgAddVec3( tmp3, dir ); - vl->add( tmp3 ); - - sgVec4 color; - sgSetVec4( color, 1.0, 0.0, 0.0, 1.0 ); - cl->add( color ); - cl->add( color ); - - ssgLeaf *leaf = - new ssgVtxTable ( GL_LINES, vl, NULL, NULL, cl ); - - SGMaterial *mat = matlib->find( "GROUND_LIGHTS" ); - leaf->setState( mat->get_state() ); - - return leaf; +static SGVasiDrawable* +buildVasi(const SGDirectionalLightBin& lights, const SGVec3f& up, + const SGVec4f& red, const SGVec4f& white) +{ + unsigned count = lights.getNumLights(); + if ( count == 4 ) { + SGVasiDrawable* drawable = new SGVasiDrawable(red, white); + + // PAPI configuration + // papi D + drawable->addLight(lights.getLight(0).position, + lights.getLight(0).normal, up, 3.5); + // papi C + drawable->addLight(lights.getLight(1).position, + lights.getLight(1).normal, up, 3.167); + // papi B + drawable->addLight(lights.getLight(2).position, + lights.getLight(2).normal, up, 2.833); + // papi A + drawable->addLight(lights.getLight(3).position, + lights.getLight(3).normal, up, 2.5); + return drawable; + } + else if (count == 12) { + SGVasiDrawable* drawable = new SGVasiDrawable(red, white); + + // probably vasi, first 6 are downwind bar (2.5 deg) + for (unsigned i = 0; i < 6; ++i) + drawable->addLight(lights.getLight(i).position, + lights.getLight(i).normal, up, 2.5); + // last 6 are upwind bar (3.0 deg) + for (unsigned i = 6; i < 12; ++i) + drawable->addLight(lights.getLight(i).position, + lights.getLight(i).normal, up, 3.0); + + return drawable; + } else { + // fail safe + SG_LOG(SG_TERRAIN, SG_ALERT, + "unknown vasi/papi configuration, count = " << count); + return 0; + } } -#endif +osg::Drawable* +SGLightFactory::getVasi(const SGVec3f& up, const SGDirectionalLightBin& lights, + const SGVec4f& red, const SGVec4f& white) +{ + SGVasiDrawable* drawable = buildVasi(lights, up, red, white); + if (!drawable) + return 0; + + osg::StateSet* stateSet = drawable->getOrCreateStateSet(); + stateSet->setRenderBinDetails(POINT_LIGHTS_BIN, "DepthSortedBin"); + stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + + osg::BlendFunc* blendFunc = new osg::BlendFunc; + stateSet->setAttribute(blendFunc); + stateSet->setMode(GL_BLEND, osg::StateAttribute::ON); + + osg::AlphaFunc* alphaFunc; + alphaFunc = new osg::AlphaFunc(osg::AlphaFunc::GREATER, 0.01); + stateSet->setAttribute(alphaFunc); + stateSet->setMode(GL_ALPHA_TEST, osg::StateAttribute::ON); + + return drawable; +} -ssgBranch *sgMakeDirectionalLights( const point_list &nodes, - const point_list &normals, - const int_list &pnt_i, - const int_list &nml_i, - SGMaterialLib *matlib, - const string &material, - sgdVec3 dup ) +osg::Node* +SGLightFactory::getSequenced(const SGDirectionalLightBin& lights) { - sgVec3 up; - sgSetVec3( up, dup ); - - sgVec3 nup; - sgNormalizeVec3( nup, up ); - - SGMaterial *mat = matlib->find( material ); - - if ( material == "RWY_REIL_LIGHTS" ) { - // cout << "found a reil" << endl; - ssgTransform *reil = gen_reil_lights( nodes, normals, pnt_i, nml_i, - matlib, up ); - return reil; - } else if ( material == "RWY_ODALS_LIGHTS" ) { - // cout << "found a odals" << endl; - ssgTransform *odals = gen_odals_lights( nodes, normals, pnt_i, nml_i, - matlib, up ); - return odals; - } else if ( material == "RWY_SEQUENCED_LIGHTS" ) { - // cout << "found a rabbit" << endl; - ssgTransform *rabbit = gen_rabbit_lights( nodes, normals, - pnt_i, nml_i, - matlib, up ); - return rabbit; - } else if ( material == "RWY_VASI_LIGHTS" ) { - ssgTransform *light_group = gen_dir_light_group( nodes, normals, pnt_i, - nml_i, mat, up, - false ); - - // calculate the geocentric position of this vasi and use it - // to init the vasi structure and save it in the userdata slot - sgdVec3 pos; - sgdSetVec3( pos, nodes[pnt_i[0]][0], nodes[pnt_i[0]][1], - nodes[pnt_i[0]][2] ); - // dup is the double version of the "up" vector which is also - // the reference center point of this tile. The reference - // center + the coordinate of the first light gives the actual - // location of the first light. - sgdAddVec3( pos, dup ); - - // extract a pointer to the leaf node so a) we can set the - // phat light call back and b) we can pass this to the vasi - // structure. - ssgRangeSelector *lod = (ssgRangeSelector *)light_group->getKid(0); - ssgLeaf *leaf = (ssgLeaf *)lod->getKid(0); - leaf->setCallback( SSG_CALLBACK_PREDRAW, VASIPreDraw ); - leaf->setCallback( SSG_CALLBACK_POSTDRAW, VASIPostDraw ); - - SGVASIUserData *vasi = new SGVASIUserData( pos, leaf ); - - light_group->setUserData( vasi ); - - return light_group; - } else if ( material == "RWY_BLUE_TAXIWAY_LIGHTS" ) { - ssgTransform *light_group = gen_dir_light_group( nodes, normals, pnt_i, - nml_i, mat, up, - true ); - return light_group; - } else { - ssgTransform *light_group = gen_dir_light_group( nodes, normals, pnt_i, - nml_i, mat, up, - false ); - return light_group; - } + if (lights.getNumLights() <= 0) + return 0; + + // generate a repeatable random seed + sg_srandom(unsigned(lights.getLight(0).position[0])); + float flashTime = 2e-2 + 5e-3*sg_random(); + osg::Sequence* sequence = new osg::Sequence; + sequence->setDefaultTime(flashTime); + Effect* effect = getLightEffect(10.0f, osg::Vec3(1.0, 0.0001, 0.00000001), + 6.0f, 10.0f, true); + for (int i = lights.getNumLights() - 1; 0 <= i; --i) { + EffectGeode* egeode = new EffectGeode; + egeode->setEffect(effect); + egeode->addDrawable(getLightDrawable(lights.getLight(i))); + sequence->addChild(egeode, flashTime); + } + sequence->addChild(new osg::Group, 1 + 1e-1*sg_random()); + sequence->setInterval(osg::Sequence::LOOP, 0, -1); + sequence->setDuration(1.0f, -1); + sequence->setMode(osg::Sequence::START); + sequence->setSync(true); + return sequence; +} - return NULL; +osg::Node* +SGLightFactory::getOdal(const SGLightBin& lights) +{ + if (lights.getNumLights() < 2) + return 0; + + // generate a repeatable random seed + sg_srandom(unsigned(lights.getLight(0).position[0])); + float flashTime = 2e-2 + 5e-3*sg_random(); + osg::Sequence* sequence = new osg::Sequence; + sequence->setDefaultTime(flashTime); + Effect* effect = getLightEffect(10.0f, osg::Vec3(1.0, 0.0001, 0.00000001), + 6.0, 10.0, false); + // centerline lights + for (int i = lights.getNumLights() - 1; 2 <= i; --i) { + EffectGeode* egeode = new EffectGeode; + egeode->setEffect(effect); + egeode->addDrawable(getLightDrawable(lights.getLight(i))); + sequence->addChild(egeode, flashTime); + } + // runway end lights + osg::Group* group = new osg::Group; + for (unsigned i = 0; i < 2; ++i) { + EffectGeode* egeode = new EffectGeode; + egeode->setEffect(effect); + egeode->addDrawable(getLightDrawable(lights.getLight(i))); + group->addChild(egeode); + } + sequence->addChild(group, flashTime); + + // add an extra empty group for a break + sequence->addChild(new osg::Group, 9 + 1e-1*sg_random()); + sequence->setInterval(osg::Sequence::LOOP, 0, -1); + sequence->setDuration(1.0f, -1); + sequence->setMode(osg::Sequence::START); + sequence->setSync(true); + + return sequence; }