X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fscene%2Ftgdb%2Fpt_lights.cxx;h=7e123a475da55583f31b65ba2dd59e28ae846fa8;hb=e4dacaf0d270fb70beb2821888945b5e837afb50;hp=e5c9a19cd6504daa2d404c6f4b983efa9728efa0;hpb=4324ffccf4bdcf34f8f8bd2f03d408fb6c958ed0;p=simgear.git diff --git a/simgear/scene/tgdb/pt_lights.cxx b/simgear/scene/tgdb/pt_lights.cxx index e5c9a19c..7e123a47 100644 --- a/simgear/scene/tgdb/pt_lights.cxx +++ b/simgear/scene/tgdb/pt_lights.cxx @@ -26,6 +26,9 @@ #include "pt_lights.hxx" +#include +#include + #include #include #include @@ -50,15 +53,27 @@ #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 @@ -123,19 +138,16 @@ getPointSpriteImage(int logResolution) return image; } +static Mutex lightMutex; + static osg::Texture2D* gen_standard_light_sprite(void) { - // double checked locking ... + // Always called from when the lightMutex is already taken static osg::ref_ptr texture; if (texture.valid()) return texture.get(); - static SGMutex mutex; - SGGuard guard(mutex); - if (texture.valid()) - return texture.get(); - texture = new osg::Texture2D; texture->setImage(getPointSpriteImage(6)); texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP); @@ -144,86 +156,102 @@ gen_standard_light_sprite(void) return texture.get(); } -SGPointSpriteLightCullCallback::SGPointSpriteLightCullCallback(const osg::Vec3& da, - float sz) : - _pointSpriteStateSet(new osg::StateSet), - _distanceAttenuationStateSet(new osg::StateSet) +namespace { - osg::PointSprite* pointSprite = new osg::PointSprite; - _pointSpriteStateSet->setTextureAttributeAndModes(0, pointSprite, - osg::StateAttribute::ON); - osg::Texture2D* texture = gen_standard_light_sprite(); - _pointSpriteStateSet->setTextureAttribute(0, texture); - _pointSpriteStateSet->setTextureMode(0, GL_TEXTURE_2D, - osg::StateAttribute::ON); - osg::TexEnv* texEnv = new osg::TexEnv; - texEnv->setMode(osg::TexEnv::MODULATE); - _pointSpriteStateSet->setTextureAttribute(0, texEnv); - - osg::Point* point = new osg::Point; - point->setFadeThresholdSize(1); - point->setMinSize(1); - point->setMaxSize(sz); - point->setSize(sz); - point->setDistanceAttenuation(da); - _distanceAttenuationStateSet->setAttributeAndModes(point); -} +typedef boost::tuple PointParams; +typedef std::map > EffectMap; -// FIXME make state sets static -SGPointSpriteLightCullCallback::SGPointSpriteLightCullCallback(osg::Point* point) : - _pointSpriteStateSet(new osg::StateSet), - _distanceAttenuationStateSet(new osg::StateSet) -{ - osg::PointSprite* pointSprite = new osg::PointSprite; - _pointSpriteStateSet->setTextureAttributeAndModes(0, pointSprite, - osg::StateAttribute::ON); - osg::Texture2D* texture = gen_standard_light_sprite(); - _pointSpriteStateSet->setTextureAttribute(0, texture); - _pointSpriteStateSet->setTextureMode(0, GL_TEXTURE_2D, - osg::StateAttribute::ON); - osg::TexEnv* texEnv = new osg::TexEnv; - texEnv->setMode(osg::TexEnv::MODULATE); - _pointSpriteStateSet->setTextureAttribute(0, texEnv); - - _distanceAttenuationStateSet->setAttributeAndModes(point); +EffectMap effectMap; + +ref_ptr polyMode = new PolygonMode(PolygonMode::FRONT, + PolygonMode::POINT); +ref_ptr pointSprite = new PointSprite; } -void -SGPointSpriteLightCullCallback::operator()(osg::Node* node, - osg::NodeVisitor* nv) +Effect* getLightEffect(float size, const Vec3& attenuation, + float minSize, float maxSize, bool directional) { - assert(dynamic_cast(nv)); - osgUtil::CullVisitor* cv = static_cast(nv); - - // Test for point sprites and point parameters availibility - unsigned contextId = cv->getRenderInfo().getContextID(); - SGSceneFeatures* features = SGSceneFeatures::instance(); - bool usePointSprite = features->getEnablePointSpriteLights(contextId); - bool usePointParameters = features->getEnableDistanceAttenuationLights(contextId); - - if (usePointSprite) - cv->pushStateSet(_pointSpriteStateSet.get()); - - if (usePointParameters) - cv->pushStateSet(_distanceAttenuationStateSet.get()); - - traverse(node, nv); - - if (usePointParameters) - cv->popStateSet(); - - if (usePointSprite) - cv->popStateSet(); + PointParams pointParams(size, attenuation, minSize, maxSize, directional); + ScopedLock lock(lightMutex); + EffectMap::iterator eitr = effectMap.find(pointParams); + if (eitr != effectMap.end()) + { + ref_ptr effect; + if (eitr->second.lock(effect)) + return effect.release(); + } + // 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()); + } + 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); + ref_ptr effect = new Effect; + std::vector parameterExtensions; + + if (SGSceneFeatures::instance()->getEnablePointSpriteLights()) + { + 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); + 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); + if (eitr == effectMap.end()) + effectMap.insert(std::make_pair(pointParams, effect)); + else + eitr->second = effect; // update existing, but empty observer + return effect.release(); } -osg::Node* -SGLightFactory::getLight(const SGLightBin::Light& light) + +osg::Drawable* +SGLightFactory::getLightDrawable(const SGLightBin::Light& light) { osg::Vec3Array* vertices = new osg::Vec3Array; osg::Vec4Array* colors = new osg::Vec4Array; - vertices->push_back(light.position.osg()); - colors->push_back(light.color.osg()); + vertices->push_back(toOsg(light.position)); + colors->push_back(toOsg(light.color)); osg::Geometry* geometry = new osg::Geometry; geometry->setVertexArray(vertices); @@ -239,28 +267,11 @@ SGLightFactory::getLight(const SGLightBin::Light& light) drawArrays = new osg::DrawArrays(osg::PrimitiveSet::POINTS, 0, vertices->size()); geometry->addPrimitiveSet(drawArrays); - - osg::StateSet* stateSet = geometry->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); - - osg::Geode* geode = new osg::Geode; - geode->addDrawable(geometry); - - return geode; + return geometry; } -osg::Node* -SGLightFactory::getLight(const SGDirectionalLightBin::Light& light) +osg::Drawable* +SGLightFactory::getLightDrawable(const SGDirectionalLightBin::Light& light) { osg::Vec3Array* vertices = new osg::Vec3Array; osg::Vec4Array* colors = new osg::Vec4Array; @@ -272,12 +283,12 @@ SGLightFactory::getLight(const SGDirectionalLightBin::Light& light) SGVec3f perp1 = perpendicular(normal); SGVec3f perp2 = cross(normal, perp1); SGVec3f position = light.position; - vertices->push_back(position.osg()); - vertices->push_back((position + perp1).osg()); - vertices->push_back((position + perp2).osg()); - colors->push_back(visibleColor.osg()); - colors->push_back(invisibleColor.osg()); - colors->push_back(invisibleColor.osg()); + 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); @@ -292,40 +303,13 @@ SGLightFactory::getLight(const SGDirectionalLightBin::Light& light) drawArrays = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES, 0, vertices->size()); geometry->addPrimitiveSet(drawArrays); - - osg::StateSet* stateSet = geometry->getOrCreateStateSet(); - stateSet->setRenderBinDetails(POINT_LIGHTS_BIN, "DepthSortedBin"); - - osg::Material* material = new osg::Material; - material->setColorMode(osg::Material::OFF); - stateSet->setAttribute(material); - - osg::CullFace* cullFace = new osg::CullFace; - cullFace->setMode(osg::CullFace::BACK); - stateSet->setAttribute(cullFace, osg::StateAttribute::ON); - stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::ON); - - osg::PolygonMode* polygonMode = new osg::PolygonMode; - polygonMode->setMode(osg::PolygonMode::FRONT, osg::PolygonMode::POINT); - stateSet->setAttribute(polygonMode); - - 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); - - osg::Geode* geode = new osg::Geode; - geode->addDrawable(geometry); - - return geode; + return geometry; } +namespace +{ + ref_ptr simpleLightSS; +} osg::Drawable* SGLightFactory::getLights(const SGLightBin& lights, unsigned inc, float alphaOff) { @@ -336,10 +320,10 @@ SGLightFactory::getLights(const SGLightBin& lights, unsigned inc, float alphaOff osg::Vec4Array* colors = new osg::Vec4Array; for (unsigned i = 0; i < lights.getNumLights(); i += inc) { - vertices->push_back(lights.getLight(i).position.osg()); + 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(color.osg()); + colors->push_back(toOsg(color)); } osg::Geometry* geometry = new osg::Geometry; @@ -353,21 +337,19 @@ SGLightFactory::getLights(const SGLightBin& lights, unsigned inc, float alphaOff drawArrays = new osg::DrawArrays(osg::PrimitiveSet::POINTS, 0, vertices->size()); geometry->addPrimitiveSet(drawArrays); - - osg::StateSet* stateSet = geometry->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); - + { + 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()); + } + } + geometry->setStateSet(simpleLightSS.get()); return geometry; } @@ -389,12 +371,12 @@ SGLightFactory::getLights(const SGDirectionalLightBin& lights) SGVec3f perp1 = perpendicular(normal); SGVec3f perp2 = cross(normal, perp1); SGVec3f position = lights.getLight(i).position; - vertices->push_back(position.osg()); - vertices->push_back((position + perp1).osg()); - vertices->push_back((position + perp2).osg()); - colors->push_back(visibleColor.osg()); - colors->push_back(invisibleColor.osg()); - colors->push_back(invisibleColor.osg()); + 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; @@ -408,34 +390,6 @@ SGLightFactory::getLights(const SGDirectionalLightBin& lights) drawArrays = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES, 0, vertices->size()); geometry->addPrimitiveSet(drawArrays); - - osg::StateSet* stateSet = geometry->getOrCreateStateSet(); - stateSet->setRenderBinDetails(POINT_LIGHTS_BIN, "DepthSortedBin"); - - osg::Material* material = new osg::Material; - material->setColorMode(osg::Material::OFF); - stateSet->setAttribute(material); - - osg::CullFace* cullFace = new osg::CullFace; - cullFace->setMode(osg::CullFace::BACK); - stateSet->setAttribute(cullFace, osg::StateAttribute::ON); - stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::ON); - - osg::PolygonMode* polygonMode = new osg::PolygonMode; - polygonMode->setMode(osg::PolygonMode::FRONT, osg::PolygonMode::POINT); - stateSet->setAttribute(polygonMode); - - 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 geometry; } @@ -461,8 +415,21 @@ buildVasi(const SGDirectionalLightBin& lights, const SGVec3f& up, drawable->addLight(lights.getLight(3).position, lights.getLight(3).normal, up, 2.5); return drawable; - } - else if (count == 12) { + + } else if (count == 6) { + SGVasiDrawable* drawable = new SGVasiDrawable(red, white); + + // probably vasi, first 3 are downwind bar (2.5 deg) + for (unsigned i = 0; i < 3; ++i) + drawable->addLight(lights.getLight(i).position, + lights.getLight(i).normal, up, 2.5); + // last 3 are upwind bar (3.0 deg) + for (unsigned i = 3; i < 6; ++i) + drawable->addLight(lights.getLight(i).position, + lights.getLight(i).normal, up, 3.0); + return drawable; + + } else if (count == 12) { SGVasiDrawable* drawable = new SGVasiDrawable(red, white); // probably vasi, first 6 are downwind bar (2.5 deg) @@ -473,8 +440,8 @@ buildVasi(const SGDirectionalLightBin& lights, const SGVec3f& up, 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, @@ -518,35 +485,19 @@ SGLightFactory::getSequenced(const SGDirectionalLightBin& lights) float flashTime = 2e-2 + 5e-3*sg_random(); osg::Sequence* sequence = new osg::Sequence; sequence->setDefaultTime(flashTime); - - for (int i = lights.getNumLights() - 1; 0 <= i; --i) - sequence->addChild(getLight(lights.getLight(i)), 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); - - osg::StateSet* stateSet = sequence->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); - - osg::Point* point = new osg::Point; - point->setMinSize(6); - point->setMaxSize(10); - point->setSize(10); - point->setDistanceAttenuation(osg::Vec3(1.0, 0.0001, 0.00000001)); - sequence->setCullCallback(new SGPointSpriteLightCullCallback(point)); - return sequence; } @@ -561,15 +512,23 @@ SGLightFactory::getOdal(const SGLightBin& lights) 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) - sequence->addChild(getLight(lights.getLight(i)), flashTime); - + 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) - group->addChild(getLight(lights.getLight(i))); + 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 @@ -579,25 +538,37 @@ SGLightFactory::getOdal(const SGLightBin& lights) sequence->setMode(osg::Sequence::START); sequence->setSync(true); - osg::StateSet* stateSet = sequence->getOrCreateStateSet(); - stateSet->setRenderBinDetails(POINT_LIGHTS_BIN, "DepthSortedBin"); - stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF); + return sequence; +} - 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); +// Blinking hold short line lights +osg::Node* +SGLightFactory::getHoldShort(const SGDirectionalLightBin& lights) +{ + if (lights.getNumLights() < 2) + return 0; + + sg_srandom(unsigned(lights.getLight(0).position[0])); + float flashTime = 2 + 0.1 * sg_random(); + osg::Sequence* sequence = new osg::Sequence; - osg::Point* point = new osg::Point; - point->setMinSize(6); - point->setMaxSize(10); - point->setSize(10); - point->setDistanceAttenuation(osg::Vec3(1.0, 0.0001, 0.00000001)); - sequence->setCullCallback(new SGPointSpriteLightCullCallback(point)); + // start with lights off + sequence->addChild(new osg::Group, flashTime); + // ...and increase the lights in steps + for (int i = 2; i < 7; i+=2) { + Effect* effect = getLightEffect(i, osg::Vec3(1, 0.001, 0.000002), + 0, i, true); + EffectGeode* egeode = new EffectGeode; + for (unsigned int j = 0; j < lights.getNumLights(); ++j) { + egeode->addDrawable(getLightDrawable(lights.getLight(j))); + egeode->setEffect(effect); + } + sequence->addChild(egeode, (i==6) ? flashTime : 0.1); + } + + sequence->setInterval(osg::Sequence::SWING, 0, -1); + sequence->setDuration(1.0f, -1); + sequence->setMode(osg::Sequence::START); return sequence; }