//
// Written by Curtis Olson, started March 2002.
//
-// Copyright (C) 2002 Curtis L. Olson - curt@flightgear.org
+// Copyright (C) 2002 Curtis L. Olson - http://www.flightgear.org/~curt
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
//
// 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 <plib/sg.h>
-
-#include <simgear/scene/material/mat.hxx>
-#include <simgear/scene/material/matlib.hxx>
-
-#include "vasi.hxx"
+#ifdef HAVE_CONFIG_H
+# include <simgear_config.h>
+#endif
#include "pt_lights.hxx"
-
-// strobe pre-draw (we want a larger point size)
-static int StrobePreDraw( ssgEntity *e ) {
- glPushAttrib( GL_POINT_BIT );
- glPointSize(3.0);
- glEnable(GL_POINT_SMOOTH);
-
- return true;
+#include <map>
+#include <boost/tuple/tuple_comparison.hpp>
+
+#include <osg/Array>
+#include <osg/Geometry>
+#include <osg/CullFace>
+#include <osg/Geode>
+#include <osg/MatrixTransform>
+#include <osg/NodeCallback>
+#include <osg/NodeVisitor>
+#include <osg/Texture2D>
+#include <osg/AlphaFunc>
+#include <osg/BlendFunc>
+#include <osg/TexEnv>
+#include <osg/Sequence>
+#include <osg/PolygonMode>
+#include <osg/Fog>
+#include <osg/FragmentProgram>
+#include <osg/VertexProgram>
+#include <osg/Point>
+#include <osg/PointSprite>
+#include <osg/Material>
+#include <osg/Group>
+#include <osg/StateSet>
+
+#include <osgUtil/CullVisitor>
+
+#include <OpenThreads/Mutex>
+#include <OpenThreads/ScopedLock>
+
+#include <simgear/math/sg_random.h>
+#include <simgear/debug/logstream.hxx>
+#include <simgear/scene/util/RenderConstants.hxx>
+#include <simgear/scene/util/SGEnlargeBoundingBox.hxx>
+#include <simgear/scene/util/StateAttributeFactory.hxx>
+
+#include <simgear/scene/material/Effect.hxx>
+#include <simgear/scene/material/EffectGeode.hxx>
+#include <simgear/scene/material/Technique.hxx>
+#include <simgear/scene/material/Pass.hxx>
+
+#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;
+ }
+ }
}
-// strobe post-draw (we want a larger point size)
-static int StrobePostDraw( ssgEntity *e ) {
- glPopAttrib();
-
- 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;
}
+static Mutex lightMutex;
-// 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" );
- }
-
- return leaf;
+static osg::Texture2D*
+gen_standard_light_sprite(void)
+{
+ // Always called from when the lightMutex is already taken
+ static osg::ref_ptr<osg::Texture2D> 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<float, osg::Vec3, float, float, bool> PointParams;
+typedef std::map<PointParams, ref_ptr<Effect> > EffectMap;
-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]; }
- }
+EffectMap effectMap;
- sgSetVec3( result, (minx + maxx) / 2.0, (miny + maxy) / 2.0,
- (minz + maxz) / 2.0 );
+ref_ptr<PolygonMode> polyMode = new PolygonMode(PolygonMode::FRONT,
+ PolygonMode::POINT);
+ref_ptr<PointSprite> pointSprite = new PointSprite;
}
-
-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 )
+Effect* getLightEffect(float size, const Vec3& attenuation,
+ float minSize, float maxSize, bool directional)
{
- 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 );
+ PointParams pointParams(size, attenuation, minSize, maxSize, directional);
+ ScopedLock<Mutex> 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());
}
-
- 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" );
+ 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.get(),
+ 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<std::string> parameterExtensions;
+
+ if (SGSceneFeatures::instance()->getEnablePointSpriteLights())
+ {
+ std::vector<std::string> 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<std::string> 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);
+ parameterExtensions.push_back(combinedExtensions.back());
}
- // 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;
+ 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_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 SGLightBin::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;
+
+ 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_odals_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;
-
- 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;
+ 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_rabbit_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;
-
- 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 );
+ ref_ptr<StateSet> simpleLightSS;
+}
+osg::Drawable*
+SGLightFactory::getLights(const SGLightBin& lights, unsigned inc, float alphaOff)
+{
+ 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<Mutex> 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, StrobePreDraw );
- // leaf->setCallback( SSG_CALLBACK_POSTDRAW, StrobePostDraw );
-
- 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;
}